Repository: conan-io/conan

Issue:
PYTHONPATH could be automatically handled
Refactoring `tools.environment_append` context manager to manage `PYTHONPATH` variable.
That way PYTHONPATH would be set automatically before `source`, `build`, `package` and `imports` from self.env_info objects. 



Discussion:
Ok, doesn't sound bad. In the past I proposed an explicit activation for the PYTHONPATH for consumers, a conservative approach, just to make sure we were not messing with the paths. I think it could be done, I would like to get the feedback of @tru here, what do you think? Better explicit in conan recipes or automatic by conan?

Resolving patch:
diff --git a/conans/client/deps_builder.py b/conans/client/deps_builder.py
--- a/conans/client/deps_builder.py
+++ b/conans/client/deps_builder.py
@@ -377,7 +377,8 @@ def _config_node(self, conanfile, conanref, down_reqs, down_ref, down_options):
         param settings: dict of settings values => {"os": "windows"}
         """
         try:
-            with get_env_context_manager(conanfile):
+            # Avoid extra time manipulating the sys.path for python
+            with get_env_context_manager(conanfile, without_python=True):
                 if hasattr(conanfile, "config"):
                     if not conanref:
                         output = ScopedOutput(str("PROJECT"), self._output)
diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py
--- a/conans/model/conan_file.py
+++ b/conans/model/conan_file.py
@@ -1,6 +1,8 @@
 import os
+from contextlib import contextmanager
 
 from conans import tools  # @UnusedImport KEEP THIS! Needed for pyinstaller to copy to exe.
+from conans.client.tools.env import pythonpath
 from conans.errors import ConanException
 from conans.model.build_info import DepsCppInfo
 from conans.model.env_info import DepsEnvInfo, EnvValues
@@ -77,8 +79,19 @@ def create_exports_sources(conanfile):
         return conanfile.exports_sources
 
 
-def get_env_context_manager(conanfile):
-    return environment_append(conanfile.env) if conanfile.apply_env else no_op()
+@contextmanager
+def _env_and_python(conanfile):
+    with environment_append(conanfile.env):
+        with pythonpath(conanfile):
+            yield
+
+
+def get_env_context_manager(conanfile, without_python=False):
+    if not conanfile.apply_env:
+        return no_op()
+    if without_python:
+        return environment_append(conanfile.env)
+    return _env_and_python(conanfile)
 
 
 class ConanFile(object):


Test patch:
diff --git a/conans/test/integration/profile_build_requires_test.py b/conans/test/integration/profile_build_requires_test.py
--- a/conans/test/integration/profile_build_requires_test.py
+++ b/conans/test/integration/profile_build_requires_test.py
@@ -50,9 +50,8 @@ class MyLib(ConanFile):
 
     def build(self):
         self.run("mytool")
-        with tools.pythonpath(self):
-            import mypythontool
-            self.output.info(mypythontool.tool_hello_world())
+        import mypythontool
+        self.output.info(mypythontool.tool_hello_world())
 """
 
 profile = """
@@ -157,9 +156,9 @@ class TestMyLib(ConanFile):
 
     def build(self):
         self.run("mytool")
-        with tools.pythonpath(self):
-            import mypythontool
-            self.output.info(mypythontool.tool_hello_world())
+        import mypythontool
+        self.output.info(mypythontool.tool_hello_world())
+
     def test(self):
         pass
         """
@@ -199,9 +198,8 @@ class MyLib(ConanFile):
     version = "0.1"
 
     def build(self):
-        with tools.pythonpath(self):
-            import mypythontool
-            self.output.info(mypythontool.tool_hello_world())
+        import mypythontool
+        self.output.info(mypythontool.tool_hello_world())
 """
         profile_patterns = """
 [build_requires]
diff --git a/conans/test/utils/python_test_files.py b/conans/test/utils/python_test_files.py
--- a/conans/test/utils/python_test_files.py
+++ b/conans/test/utils/python_test_files.py
@@ -15,8 +15,7 @@ class {name}Conan(ConanFile):
     build_policy = "missing"
 
     def build(self):
-        with tools.pythonpath(self):
-            pass
+        pass
 {build}
 
     def package(self):
@@ -77,8 +76,8 @@ def py_hello_conan_files(name, version, deps=None):
     requires = ", ".join('"%s"' % r for r in requires)
     deps_names = [str(n).split("/", 1)[0] for n in deps or []]
     if deps:
-        build = "\n".join(["            from hello%s import hello as h%s\n"
-                           "            h%s.build_helper(self)" % (i, i, i) for i in deps_names])
+        build = "\n".join(["        from hello%s import hello as h%s\n"
+                           "        h%s.build_helper(self)" % (i, i, i) for i in deps_names])
     else:
         build = ""
     conanfile = conanfile_template.format(name=name, version=version, requires=requires,