Repository: conan-io/conan
Pull request #3097 (2018-06-22 13:38:54)

Provide a user-agent header for Conan (#3390)
- [X] Refer to the issue that supports this Pull Request: Closes #3097 

Docs: https://github.com/conan-io/docs/pull/700

This PR generates a user agent string that outputs the Conan client version and the Python version.  This will allow us to track data for client version uptake, and data needed for assessing python version deprecation.  Example: Conan/1.5.0 (Python 2.7.1)

I have not implemented tests yet.  I did do manual tests to confirm it achieves the desired result against Bintray, and when requesting a source tarball from places like source forge.


Patch:
diff --git a/conans/client/build/cmake.py b/conans/client/build/cmake.py
index 1703d4345..c26134322 100644
--- a/conans/client/build/cmake.py
+++ b/conans/client/build/cmake.py
@@ -340,6 +340,9 @@ class CMake(object):
         if "cmake_find_package" in self._conanfile.generators:
             ret["CMAKE_MODULE_PATH"] = self._conanfile.install_folder.replace("\\", "/")
 
+        # Disable CMake export registry #3070 (CMake installing modules in user home's)
+        ret["CMAKE_EXPORT_NO_PACKAGE_REGISTRY"] = "ON"
+
         return ret
 
     def _get_dirs(self, source_folder, build_folder, source_dir, build_dir, cache_build_folder):
diff --git a/conans/client/cmd/export.py b/conans/client/cmd/export.py
index ed8a2e3e2..ac240f57e 100644
--- a/conans/client/cmd/export.py
+++ b/conans/client/cmd/export.py
@@ -113,6 +113,9 @@ def _capture_export_scm_data(conanfile, src_path, destination_folder, output, pa
         origin = scm.get_remote_url()
         if not origin:
             raise ConanException("Repo origin cannot be deduced by 'auto'")
+        if os.path.exists(origin):
+            output.warn("Repo origin looks like a local path: %s" % origin)
+            origin = origin.replace("\\", "/")
         output.success("Repo origin deduced by 'auto': %s" % origin)
         scm.url = origin
     if scm.revision == "auto":
diff --git a/conans/client/cmd/search.py b/conans/client/cmd/search.py
index 77ee84fb8..f6f42d3b4 100644
--- a/conans/client/cmd/search.py
+++ b/conans/client/cmd/search.py
@@ -3,6 +3,7 @@ from conans.search.search import filter_outdated, search_recipes,\
 from collections import OrderedDict, namedtuple
 from conans.errors import NotFoundException
 
+
 class Search(object):
     def __init__(self, client_cache, remote_manager, remote_registry):
         self._client_cache = client_cache
@@ -34,6 +35,7 @@ class Search(object):
         return references
 
     remote_ref = namedtuple('remote_ref', 'ordered_packages recipe_hash')
+
     def search_packages(self, reference=None, remote_name=None, query=None, outdated=False):
         """ Return the single information saved in conan.vars about all the packages
             or the packages which match with a pattern
@@ -88,7 +90,7 @@ class Search(object):
                 except NotFoundException:
                     continue
             return references
-        
+
         return self._search_packages_in(self, 'all', reference, query, outdated)
 
     def _search_packages_in(self, remote_name, reference=None, query=None, outdated=False):
@@ -103,4 +105,4 @@ class Search(object):
 
         references = OrderedDict()
         references[remote.name] = self.remote_ref(ordered_packages, recipe_hash)
-        return references
\ No newline at end of file
+        return references
diff --git a/conans/client/conan_api.py b/conans/client/conan_api.py
index df7e73e45..3b8b2ea5a 100644
--- a/conans/client/conan_api.py
+++ b/conans/client/conan_api.py
@@ -723,10 +723,10 @@ class ConanAPIV1(object):
                 for package_id, properties in remote_ref.ordered_packages.items():
                     package_recipe_hash = properties.get("recipe_hash", None)
                     recorder.add_package(str(remote), str(reference), package_id,
-                                     properties.get("options", []),
-                                     properties.get("settings", []),
-                                     properties.get("full_requires", []),
-                                     remote_ref.recipe_hash != package_recipe_hash)
+                                         properties.get("options", []),
+                                         properties.get("settings", []),
+                                         properties.get("full_requires", []),
+                                         remote_ref.recipe_hash != package_recipe_hash)
         return recorder.get_info()
 
     @api_method
diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py
index 2be13e19e..f210659e9 100644
--- a/conans/client/conf/__init__.py
+++ b/conans/client/conf/__init__.py
@@ -31,6 +31,7 @@ os:
         version: ["8.1", "10.0"]
     Linux:
     Macos:
+        version: [None, "10.6", "10.7", "10.8", "10.9", "10.10", "10.11", "10.12", "10.13", "10.14"]
     Android:
         api_level: ANY
     iOS:
@@ -66,7 +67,7 @@ compiler:
         version: ["3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4.0", "5.0", "6.0", "7.0"]
         libcxx: [libstdc++, libstdc++11, libc++]
     apple-clang:
-        version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0", "8.1", "9.0", "9.1"]
+        version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0", "8.1", "9.0", "9.1", "10.0"]
         libcxx: [libstdc++, libc++]
 
 build_type: [None, Debug, Release, RelWithDebInfo, MinSizeRel]
diff --git a/conans/client/conf/detect.py b/conans/client/conf/detect.py
index a12341d72..5a39c0646 100644
--- a/conans/client/conf/detect.py
+++ b/conans/client/conf/detect.py
@@ -61,7 +61,7 @@ def _clang_compiler(output, compiler_exe="clang"):
             compiler = "apple-clang"
         elif "clang version" in out:
             compiler = "clang"
-        installed_version = re.search("([0-9]\.[0-9])", out).group()
+        installed_version = re.search("([0-9]+\.[0-9])", out).group()
         if installed_version:
             output.success("Found %s %s" % (compiler, installed_version))
             return compiler, installed_version
diff --git a/conans/client/migrations.py b/conans/client/migrations.py
index 9a7b586a8..6d132a89d 100644
--- a/conans/client/migrations.py
+++ b/conans/client/migrations.py
@@ -42,15 +42,17 @@ class ClientMigrator(Migrator):
         # VERSION 0.1
         if old_version is None:
             return
-        if old_version < Version("1.4.0"):
+        if old_version < Version("1.5.0"):
             old_settings = """
 # Only for cross building, 'os_build/arch_build' is the system that runs Conan
 os_build: [Windows, WindowsStore, Linux, Macos, FreeBSD, SunOS]
 arch_build: [x86, x86_64, ppc64le, ppc64, armv6, armv7, armv7hf, armv8, sparc, sparcv9, mips, mips64, avr, armv7s, armv7k]
+
 # Only for building cross compilation tools, 'os_target/arch_target' is the system for
 # which the tools generate code
 os_target: [Windows, Linux, Macos, Android, iOS, watchOS, tvOS, FreeBSD, SunOS, Arduino]
 arch_target: [x86, x86_64, ppc64le, ppc64, armv6, armv7, armv7hf, armv8, sparc, sparcv9, mips, mips64, avr, armv7s, armv7k]
+
 # Rest of the settings are "host" settings:
 # - For native building/cross building: Where the library/program will run.
 # - For building cross compilation tools: Where the cross compiler will run.
@@ -83,7 +85,8 @@ compiler:
         version: ["4.1", "4.4", "4.5", "4.6", "4.7", "4.8", "4.9",
                   "5", "5.1", "5.2", "5.3", "5.4", "5.5",
                   "6", "6.1", "6.2", "6.3", "6.4",
-                  "7", "7.1", "7.2", "7.3"]
+                  "7", "7.1", "7.2", "7.3",
+                  "8", "8.1"]
         libcxx: [libstdc++, libstdc++11]
         threads: [None, posix, win32] #  Windows MinGW
         exception: [None, dwarf2, sjlj, seh] # Windows MinGW
@@ -92,13 +95,14 @@ compiler:
         version: ["8", "9", "10", "11", "12", "14", "15"]
         toolset: [None, v90, v100, v110, v110_xp, v120, v120_xp, v140, v140_xp, v140_clang_c2, LLVM-vs2014, LLVM-vs2014_xp, v141, v141_xp, v141_clang_c2]
     clang:
-        version: ["3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4.0", "5.0", "6.0"]
+        version: ["3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4.0", "5.0", "6.0", "7.0"]
         libcxx: [libstdc++, libstdc++11, libc++]
     apple-clang:
         version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0", "8.1", "9.0", "9.1"]
         libcxx: [libstdc++, libc++]
+
 build_type: [None, Debug, Release, RelWithDebInfo, MinSizeRel]
-cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17]
+cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20]
 """
             self._update_settings_yml(old_settings)
 
diff --git a/conans/client/printer.py b/conans/client/printer.py
index c1504423c..1be4ec588 100644
--- a/conans/client/printer.py
+++ b/conans/client/printer.py
@@ -185,7 +185,7 @@ class Printer(object):
                             str(reference)
                 self._out.info(warn_msg)
                 continue
-                
+
             reference = remote_info["items"][0]["recipe"]["id"]
             packages = remote_info["items"][0]["packages"]
 
diff --git a/conans/client/rest/conan_requester.py b/conans/client/rest/conan_requester.py
index 82686d110..bab00d483 100644
--- a/conans/client/rest/conan_requester.py
+++ b/conans/client/rest/conan_requester.py
@@ -1,15 +1,17 @@
 import fnmatch
 import os
+import requests
+import platform
 
 from conans.util.files import save
-
+from conans import __version__ as client_version
 
 class ConanRequester(object):
 
     def __init__(self, requester, client_cache, timeout):
         self.proxies = client_cache.conan_config.proxies or {}
         self._no_proxy_match = [el.strip() for el in
-                                self.proxies.pop("no_proxy_match", "").split(",")]
+                                self.proxies.pop("no_proxy_match", "").split(",") if el]
         self._timeout_seconds = timeout
 
         # Retrocompatibility with deprecated no_proxy
@@ -55,17 +57,36 @@ class ConanRequester(object):
                 kwargs["proxies"] = self.proxies
         if self._timeout_seconds:
             kwargs["timeout"] = self._timeout_seconds
+        if not kwargs.get("headers"):
+            kwargs["headers"]  = {}
+        kwargs["headers"]["User-Agent"] = "Conan/%s (Python %s) %s" % (client_version,
+                                                                       platform.python_version(),
+                                                                       requests.utils.default_user_agent())
         return kwargs
 
     def get(self, url, **kwargs):
-        return self._requester.get(url, **self._add_kwargs(url, kwargs))
+        return self._call_method("get", url, **kwargs)
 
     def put(self, url, **kwargs):
-        return self._requester.put(url, **self._add_kwargs(url, kwargs))
+        return self._call_method("put", url, **kwargs)
 
     def delete(self, url, **kwargs):
-        return self._requester.delete(url, **self._add_kwargs(url, kwargs))
+        return self._call_method("delete", url, **kwargs)
 
     def post(self, url, **kwargs):
-        return self._requester.post(url, **self._add_kwargs(url, kwargs))
-
+        return self._call_method("post", url, **kwargs)
+
+    def _call_method(self, method, url, **kwargs):
+        popped = False
+        if self.proxies or self._no_proxy_match:
+            old_env = dict(os.environ)
+            # Clean the proxies from the environ and use the conan specified proxies
+            for var_name in ("http_proxy", "https_proxy", "no_proxy"):
+                popped = popped or os.environ.pop(var_name, None)
+                popped = popped or os.environ.pop(var_name.upper(), None)
+        try:
+            return getattr(self._requester, method)(url, **self._add_kwargs(url, kwargs))
+        finally:
+            if popped:
+                os.environ.clear()
+                os.environ.update(old_env)
diff --git a/conans/client/tools/win.py b/conans/client/tools/win.py
index 2cfcd3741..d02923871 100644
--- a/conans/client/tools/win.py
+++ b/conans/client/tools/win.py
@@ -5,6 +5,7 @@ import re
 import deprecation
 
 import subprocess
+
 from contextlib import contextmanager
 
 from conans.client.tools.env import environment_append
@@ -136,8 +137,6 @@ def vswhere(all_=False, prerelease=False, products=None, requires=None, version=
 
     program_files = os.environ.get("ProgramFiles(x86)", os.environ.get("ProgramFiles"))
 
-    vswhere_path = ""
-
     if program_files:
         vswhere_path = os.path.join(program_files, "Microsoft Visual Studio", "Installer",
                                     "vswhere.exe")
@@ -186,11 +185,16 @@ def vswhere(all_=False, prerelease=False, products=None, requires=None, version=
 
     try:
         output = subprocess.check_output(arguments)
-        vswhere_out = decode_text(output).strip()
+        output = decode_text(output).strip()
+        # Ignore the "description" field, that even decoded contains non valid charsets for json
+        # (ignored ones)
+        output = "\n".join([line for line in output.splitlines()
+                            if not line.strip().startswith('"description"')])
+
     except (ValueError, subprocess.CalledProcessError, UnicodeDecodeError) as e:
         raise ConanException("vswhere error: %s" % str(e))
 
-    return json.loads(vswhere_out)
+    return json.loads(output)
 
 
 def vs_comntools(compiler_version):
diff --git a/conans/search/search.py b/conans/search/search.py
index f8beea941..975a97403 100644
--- a/conans/search/search.py
+++ b/conans/search/search.py
@@ -1,7 +1,6 @@
 import re
 import os
 
-
 from fnmatch import translate
 
 from conans.errors import ConanException, NotFoundException
@@ -89,12 +88,27 @@ def search_recipes(paths, pattern=None, ignorecase=True):
         ret = []
         for subdir in subdirs:
             conan_ref = ConanFileReference(*subdir.split("/"))
-            if pattern:
-                if pattern.match(str(conan_ref)):
-                    ret.append(conan_ref)
+            if _partial_match(pattern, conan_ref):
+                ret.append(conan_ref)
+
         return sorted(ret)
 
 
+def _partial_match(pattern, conan_ref):
+    """
+    Finds if pattern matches any of partial sums of tokens of conan reference
+    """
+    tokens = str(conan_ref).replace('/', ' / ').replace('@', ' @ ').split()
+
+    def partial_sums(iterable):
+        partial = ''
+        for i in iterable:
+            partial += i
+            yield partial
+
+    return any(map(pattern.match, list(partial_sums(tokens))))
+
+
 def search_packages(paths, reference, query):
     """ Return a dict like this:
 
diff --git a/conans/util/files.py b/conans/util/files.py
index 8c6a859a1..74982ea69 100644
--- a/conans/util/files.py
+++ b/conans/util/files.py
@@ -194,7 +194,7 @@ def _change_permissions(func, path, exc_info):
         os.chmod(path, stat.S_IWUSR)
         func(path)
     else:
-        raise
+        raise OSError("Cannot change permissions for {}! Exception info: {}".format(path, exc_info))
 
 
 def rmdir(path):


Test patch:
diff --git a/conans/test/build_helpers/cmake_test.py b/conans/test/build_helpers/cmake_test.py
index 4b5d400a9..f9d8e70bd 100644
--- a/conans/test/build_helpers/cmake_test.py
+++ b/conans/test/build_helpers/cmake_test.py
@@ -183,7 +183,8 @@ class CMakeTest(unittest.TestCase):
                        '-DCONAN_EXPORTED="1" -DCONAN_COMPILER="gcc" ' \
                        '-DCONAN_COMPILER_VERSION="6.3" ' \
                        '-DCONAN_CXX_FLAGS="-m32" -DCONAN_SHARED_LINKER_FLAGS="-m32" ' \
-                       '-DCONAN_C_FLAGS="-m32" -Wno-dev ' % (generator, linux_stuff)
+                       '-DCONAN_C_FLAGS="-m32" -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" ' \
+                       '-Wno-dev ' % (generator, linux_stuff)
             build_expected = quote_var("build")
             source_expected = quote_var("../subdir")
 
@@ -293,30 +294,37 @@ class CMakeTest(unittest.TestCase):
                 if "Visual Studio" in text:
                     cores = ('-DCONAN_CXX_FLAGS="/MP{0}" '
                              '-DCONAN_C_FLAGS="/MP{0}" '.format(tools.cpu_count()))
-                    new_text = new_text.replace("-Wno-dev", "%s-Wno-dev" % cores)
+                    new_text = new_text.replace('-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON"',
+                                                '%s-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON"' % cores)
+
                 self.assertEqual(new_text, cmake.command_line)
                 self.assertEqual(build_config, cmake.build_config)
 
         check('-G "Visual Studio 12 2013" -DCONAN_EXPORTED="1" '
-              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" -Wno-dev',
+              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev',
               "")
 
         check('-G "Custom Generator" -DCONAN_EXPORTED="1" '
-              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" -Wno-dev',
+              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev',
               '', generator="Custom Generator")
 
         check('-G "Custom Generator" -DCONAN_EXPORTED="1" '
-              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" -Wno-dev',
+              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev',
               '', generator="Custom Generator", set_cmake_flags=True)
 
         settings.build_type = "Debug"
         check('-G "Visual Studio 12 2013" -DCONAN_EXPORTED="1" '
-              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" -Wno-dev',
+              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev',
               '--config Debug')
 
         settings.arch = "x86_64"
         check('-G "Visual Studio 12 2013 Win64" -DCONAN_EXPORTED="1" '
-              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" -Wno-dev',
+              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev',
               '--config Debug')
 
         settings.compiler = "gcc"
@@ -324,21 +332,24 @@ class CMakeTest(unittest.TestCase):
         generator = "MinGW Makefiles" if platform.system() == "Windows" else "Unix Makefiles"
         check('-G "%s" -DCMAKE_BUILD_TYPE="Debug" -DCONAN_EXPORTED="1" '
               '-DCONAN_COMPILER="gcc" -DCONAN_COMPILER_VERSION="4.8" -DCONAN_CXX_FLAGS="-m64" '
-              '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" -Wno-dev' % generator, "")
+              '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev' % generator, "")
 
         settings.os = "Linux"
         settings.arch = "x86"
         check('-G "%s" -DCMAKE_BUILD_TYPE="Debug"'
               ' -DCONAN_EXPORTED="1" -DCONAN_COMPILER="gcc" '
               '-DCONAN_COMPILER_VERSION="4.8" -DCONAN_CXX_FLAGS="-m32" '
-              '-DCONAN_SHARED_LINKER_FLAGS="-m32" -DCONAN_C_FLAGS="-m32" -Wno-dev' % generator,
+              '-DCONAN_SHARED_LINKER_FLAGS="-m32" -DCONAN_C_FLAGS="-m32" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev' % generator,
               "")
 
         settings.arch = "x86_64"
         check('-G "%s" -DCMAKE_BUILD_TYPE="Debug"'
               ' -DCONAN_EXPORTED="1" -DCONAN_COMPILER="gcc" '
               '-DCONAN_COMPILER_VERSION="4.8" -DCONAN_CXX_FLAGS="-m64" '
-              '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" -Wno-dev' % generator,
+              '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev' % generator,
               "")
 
         check('-G "%s" -DCMAKE_BUILD_TYPE="Debug"'
@@ -346,6 +357,7 @@ class CMakeTest(unittest.TestCase):
               '-DCONAN_COMPILER_VERSION="4.8" -DCONAN_CXX_FLAGS="-m64" '
               '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" '
               '-DCMAKE_CXX_FLAGS="-m64" -DCMAKE_SHARED_LINKER_FLAGS="-m64" -DCMAKE_C_FLAGS="-m64" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" '
               '-Wno-dev' % generator,
               "", set_cmake_flags=True)
 
@@ -356,14 +368,16 @@ class CMakeTest(unittest.TestCase):
         check('-G "%s" -DCMAKE_BUILD_TYPE="Debug"'
               ' -DCONAN_EXPORTED="1" -DCONAN_COMPILER="clang" '
               '-DCONAN_COMPILER_VERSION="3.8" -DCONAN_CXX_FLAGS="-m32" '
-              '-DCONAN_SHARED_LINKER_FLAGS="-m32" -DCONAN_C_FLAGS="-m32" -Wno-dev' % generator,
+              '-DCONAN_SHARED_LINKER_FLAGS="-m32" -DCONAN_C_FLAGS="-m32" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev' % generator,
               "")
 
         settings.arch = "x86_64"
         check('-G "%s" -DCMAKE_BUILD_TYPE="Debug"'
               ' -DCONAN_EXPORTED="1" -DCONAN_COMPILER="clang" '
               '-DCONAN_COMPILER_VERSION="3.8" -DCONAN_CXX_FLAGS="-m64" '
-              '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" -Wno-dev' % generator,
+              '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev' % generator,
               "")
 
         settings.os = "SunOS"
@@ -373,14 +387,16 @@ class CMakeTest(unittest.TestCase):
         check('-G "%s" -DCMAKE_BUILD_TYPE="Debug"'
               ' -DCONAN_EXPORTED="1" -DCONAN_COMPILER="sun-cc" '
               '-DCONAN_COMPILER_VERSION="5.10" -DCONAN_CXX_FLAGS="-m32" '
-              '-DCONAN_SHARED_LINKER_FLAGS="-m32" -DCONAN_C_FLAGS="-m32" -Wno-dev' % generator,
+              '-DCONAN_SHARED_LINKER_FLAGS="-m32" -DCONAN_C_FLAGS="-m32" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev' % generator,
               "")
 
         settings.arch = "x86_64"
         check('-G "%s" -DCMAKE_BUILD_TYPE="Debug"'
               ' -DCONAN_EXPORTED="1" -DCONAN_COMPILER="sun-cc" '
               '-DCONAN_COMPILER_VERSION="5.10" -DCONAN_CXX_FLAGS="-m64" '
-              '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" -Wno-dev' % generator,
+              '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev' % generator,
               "")
 
         settings.arch = "sparc"
@@ -388,14 +404,16 @@ class CMakeTest(unittest.TestCase):
         check('-G "%s" -DCMAKE_BUILD_TYPE="Debug" -DCONAN_EXPORTED="1" '
               '-DCONAN_COMPILER="sun-cc" '
               '-DCONAN_COMPILER_VERSION="5.10" -DCONAN_CXX_FLAGS="-m32" '
-              '-DCONAN_SHARED_LINKER_FLAGS="-m32" -DCONAN_C_FLAGS="-m32" -Wno-dev' % generator,
+              '-DCONAN_SHARED_LINKER_FLAGS="-m32" -DCONAN_C_FLAGS="-m32" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev' % generator,
               "")
 
         settings.arch = "sparcv9"
         check('-G "%s" -DCMAKE_BUILD_TYPE="Debug" -DCONAN_EXPORTED="1" '
               '-DCONAN_COMPILER="sun-cc" '
               '-DCONAN_COMPILER_VERSION="5.10" -DCONAN_CXX_FLAGS="-m64" '
-              '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" -Wno-dev' % generator,
+              '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev' % generator,
               "")
 
         settings.compiler = "Visual Studio"
@@ -404,12 +422,14 @@ class CMakeTest(unittest.TestCase):
         settings.os.version = "8.1"
         settings.build_type = "Debug"
         check('-G "Visual Studio 12 2013" -DCONAN_EXPORTED="1" '
-              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" -Wno-dev',
+              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev',
               "--config Debug")
 
         settings.os.version = "10.0"
         check('-G "Visual Studio 12 2013" -DCONAN_EXPORTED="1" '
-              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" -Wno-dev',
+              '-DCONAN_COMPILER="Visual Studio" -DCONAN_COMPILER_VERSION="12" '
+              '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev',
               "--config Debug")
 
     def deleted_os_test(self):
@@ -434,7 +454,8 @@ build_type: [ Release]
         cross = "-DCMAKE_SYSTEM_NAME=\"Linux\" -DCMAKE_SYSROOT=\"/path/to/sysroot\" " if platform.system() != "Linux" else ""
         self.assertEqual('-G "%s Makefiles" %s-DCONAN_EXPORTED="1" -DCONAN_COMPILER="gcc" '
                          '-DCONAN_COMPILER_VERSION="4.9" -DCONAN_CXX_FLAGS="-m64" '
-                         '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" -Wno-dev' % (generator, cross),
+                         '-DCONAN_SHARED_LINKER_FLAGS="-m64" -DCONAN_C_FLAGS="-m64" '
+                         '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" -Wno-dev' % (generator, cross),
                          cmake.command_line)
 
     def test_sysroot(self):
@@ -518,8 +539,9 @@ build_type: [ Release]
 
         self.assertEqual('cd {0} && cmake -G "MinGW Makefiles" '
                          '{1} -DCONAN_EXPORTED="1"'
-                         ' -DCONAN_COMPILER="gcc" -DCONAN_COMPILER_VERSION="5.4" '
-                         '-Wno-dev {0}'.format(dot_dir, cross),
+                         ' -DCONAN_COMPILER="gcc" -DCONAN_COMPILER_VERSION="5.4"'
+                         ' -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON"'
+                         ' -Wno-dev {0}'.format(dot_dir, cross),
                          conan_file.command)
 
         cmake.build()
@@ -552,6 +574,7 @@ build_type: [ Release]
         self.assertEqual('cd %s && cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE="Debug" '
                          '%s -DCONAN_EXPORTED="1" '
                          '-DCONAN_COMPILER="gcc" -DCONAN_COMPILER_VERSION="5.4" '
+                         '-DCMAKE_EXPORT_NO_PACKAGE_REGISTRY="ON" '
                          '-Wno-dev %s' % (tempdir, cross, escaped_args),
                          conan_file.command)
 
diff --git a/conans/test/build_helpers/cpp_std_flags_test.py b/conans/test/build_helpers/cpp_std_flags_test.py
index 6d80880d2..70f09e990 100644
--- a/conans/test/build_helpers/cpp_std_flags_test.py
+++ b/conans/test/build_helpers/cpp_std_flags_test.py
@@ -148,6 +148,8 @@ class CompilerFlagsTest(unittest.TestCase):
         self.assertEquals(cppstd_flag("apple-clang", "9.1", "14"), '-std=c++14')
         self.assertEquals(cppstd_flag("apple-clang", "9.1", "17"), "-std=c++17")
 
+        self.assertEquals(cppstd_flag("apple-clang", "10.0", "17"), "-std=c++17")
+
     def test_apple_clang_cppstd_defaults(self):
         self.assertEquals(cppstd_default("apple-clang", "2"), "gnu98")
         self.assertEquals(cppstd_default("apple-clang", "3"), "gnu98")
@@ -157,6 +159,7 @@ class CompilerFlagsTest(unittest.TestCase):
         self.assertEquals(cppstd_default("apple-clang", "7"), "gnu98")
         self.assertEquals(cppstd_default("apple-clang", "8"), "gnu98")
         self.assertEquals(cppstd_default("apple-clang", "9"), "gnu98")
+        self.assertEquals(cppstd_default("apple-clang", "10"), "gnu98")
 
     def test_visual_cppstd_flags(self):
         self.assertEquals(cppstd_flag("Visual Studio", "12", "11"), None)
diff --git a/conans/test/command/search_test.py b/conans/test/command/search_test.py
index af7c83dde..e94731ce9 100644
--- a/conans/test/command/search_test.py
+++ b/conans/test/command/search_test.py
@@ -90,7 +90,7 @@ class SearchTest(unittest.TestCase):
 
         # No conans created
         self.client.run("search")
-        output = self.client.user_io.out
+        output = self.client.out
         self.assertIn('There are no packages', output)
 
         # Conans with and without packages created
@@ -169,14 +169,14 @@ helloTest/1.4.10@fenix/stable""".format(remote)
                           "Hello/1.4.10@fenix/testing\n"
                           "Hello/1.4.11@fenix/testing\n"
                           "Hello/1.4.12@fenix/testing\n"
-                          "helloTest/1.4.10@fenix/stable\n", self.client.user_io.out)
+                          "helloTest/1.4.10@fenix/stable\n", self.client.out)
 
         self.client.run("search Hello* --case-sensitive")
         self.assertEquals("Existing package recipes:\n\n"
                           "Hello/1.4.10@fenix/testing\n"
                           "Hello/1.4.11@fenix/testing\n"
                           "Hello/1.4.12@fenix/testing\n",
-                          self.client.user_io.out)
+                          self.client.out)
 
         self.client.run("search *fenix* --case-sensitive")
         self.assertEquals("Existing package recipes:\n\n"
@@ -186,19 +186,73 @@ helloTest/1.4.10@fenix/stable""".format(remote)
                           "Hello/1.4.12@fenix/testing\n"
                           "MissFile/1.0.2@fenix/stable\n"
                           "NodeInfo/1.0.2@fenix/stable\n"
-                          "helloTest/1.4.10@fenix/stable\n", self.client.user_io.out)
+                          "helloTest/1.4.10@fenix/stable\n", self.client.out)
 
         self.client.run("search Hello/*@fenix/testing")
         self.assertIn("Hello/1.4.10@fenix/testing\n"
                       "Hello/1.4.11@fenix/testing\n"
-                      "Hello/1.4.12@fenix/testing\n", self.client.user_io.out)
+                      "Hello/1.4.12@fenix/testing\n", self.client.out)
+
+    def search_partial_match_test(self):
+        self.client.run("search Hello")
+        self.assertEquals("Existing package recipes:\n\n"
+                          "Hello/1.4.10@fenix/testing\n"
+                          "Hello/1.4.11@fenix/testing\n"
+                          "Hello/1.4.12@fenix/testing\n", self.client.out)
+
+        self.client.run("search hello")
+        self.assertEquals("Existing package recipes:\n\n"
+                          "Hello/1.4.10@fenix/testing\n"
+                          "Hello/1.4.11@fenix/testing\n"
+                          "Hello/1.4.12@fenix/testing\n", self.client.out)
+
+        self.client.run("search Hello --case-sensitive")
+        self.assertEquals("Existing package recipes:\n\n"
+                          "Hello/1.4.10@fenix/testing\n"
+                          "Hello/1.4.11@fenix/testing\n"
+                          "Hello/1.4.12@fenix/testing\n", self.client.out)
+
+        self.client.run("search Hel")
+        self.assertEquals("There are no packages matching the 'Hel' pattern\n", self.client.out)
+
+        self.client.run("search Hello/")
+        self.assertEquals("Existing package recipes:\n\n"
+                          "Hello/1.4.10@fenix/testing\n"
+                          "Hello/1.4.11@fenix/testing\n"
+                          "Hello/1.4.12@fenix/testing\n", self.client.out)
+
+        self.client.run("search Hello/1.4.10")
+        self.assertEquals("Existing package recipes:\n\n"
+                          "Hello/1.4.10@fenix/testing\n", self.client.out)
+
+        self.client.run("search Hello/1.4")
+        self.assertEquals("There are no packages matching the 'Hello/1.4' pattern\n", self.client.out)
+
+        self.client.run("search Hello/1.4.10@")
+        self.assertEquals("Existing package recipes:\n\n"
+                          "Hello/1.4.10@fenix/testing\n", self.client.out)
+
+        self.client.run("search Hello/1.4.10@fenix")
+        self.assertEquals("Existing package recipes:\n\n"
+                          "Hello/1.4.10@fenix/testing\n", self.client.out)
+
+        self.client.run("search Hello/1.4.10@fen")
+        self.assertEquals("There are no packages matching the 'Hello/1.4.10@fen' pattern\n", self.client.out)
+
+        self.client.run("search Hello/1.4.10@fenix/")
+        self.assertEquals("Existing package recipes:\n\n"
+                          "Hello/1.4.10@fenix/testing\n", self.client.out)
+
+        error = self.client.run("search Hello/1.4.10@fenix/test", ignore_error=True)
+        self.assertTrue(error)
+        self.assertEquals("ERROR: Recipe not found: Hello/1.4.10@fenix/test\n", self.client.out)
 
     def search_raw_test(self):
         self.client.run("search Hello* --raw")
         self.assertEquals("Hello/1.4.10@fenix/testing\n"
                           "Hello/1.4.11@fenix/testing\n"
                           "Hello/1.4.12@fenix/testing\n"
-                          "helloTest/1.4.10@fenix/stable\n", self.client.user_io.out)
+                          "helloTest/1.4.10@fenix/stable\n", self.client.out)
 
     def search_html_table_test(self):
         self.client.run("search Hello/1.4.10/fenix/testing --table=table.html")
@@ -206,7 +260,7 @@ helloTest/1.4.10@fenix/stable""".format(remote)
         self.assertIn("<h1>Hello/1.4.10@fenix/testing</h1>", html)
         self.assertIn("<td>Linux gcc 4.5 (libstdc++11)</td>", html)
         self.assertIn("<td>Windows Visual Studio 8.1</td>", html)
-        
+
     def search_html_table_all_test(self):
         os.rmdir(self.servers["local"].paths.store)
         shutil.copytree(self.client.paths.store, self.servers["local"].paths.store)
@@ -226,7 +280,7 @@ helloTest/1.4.10@fenix/stable""".format(remote)
     def search_html_table_with_no_reference_test(self):
         self.client.run("search Hello* --table=table.html", ignore_error=True)
         self.assertIn("ERROR: '--table' argument can only be used with a reference",
-                      self.client.user_io.out)
+                      self.client.out)
 
     def recipe_pattern_search_test(self):
         self.client.run("search Hello*")
@@ -234,13 +288,13 @@ helloTest/1.4.10@fenix/stable""".format(remote)
                           "Hello/1.4.10@fenix/testing\n"
                           "Hello/1.4.11@fenix/testing\n"
                           "Hello/1.4.12@fenix/testing\n"
-                          "helloTest/1.4.10@fenix/stable\n", self.client.user_io.out)
+                          "helloTest/1.4.10@fenix/stable\n", self.client.out)
 
         self.client.run("search Hello* --case-sensitive")
         self.assertEquals("Existing package recipes:\n\n"
                           "Hello/1.4.10@fenix/testing\n"
                           "Hello/1.4.11@fenix/testing\n"
-                          "Hello/1.4.12@fenix/testing\n", self.client.user_io.out)
+                          "Hello/1.4.12@fenix/testing\n", self.client.out)
 
         self.client.run("search *fenix* --case-sensitive")
         self.assertEquals("Existing package recipes:\n\n"
@@ -250,58 +304,58 @@ helloTest/1.4.10@fenix/stable""".format(remote)
                           "Hello/1.4.12@fenix/testing\n"
                           "MissFile/1.0.2@fenix/stable\n"
                           "NodeInfo/1.0.2@fenix/stable\n"
-                          "helloTest/1.4.10@fenix/stable\n", self.client.user_io.out)
+                          "helloTest/1.4.10@fenix/stable\n", self.client.out)
 
     def package_search_with_invalid_reference_test(self):
         self.client.run("search Hello -q 'a=1'", ignore_error=True)
-        self.assertIn("-q parameter only allowed with a valid recipe", str(self.client.user_io.out))
+        self.assertIn("-q parameter only allowed with a valid recipe", str(self.client.out))
 
     def package_search_with_empty_query_test(self):
         self.client.run("search Hello/1.4.10/fenix/testing")
-        self.assertIn("WindowsPackageSHA", self.client.user_io.out)
-        self.assertIn("PlatformIndependantSHA", self.client.user_io.out)
-        self.assertIn("LinuxPackageSHA", self.client.user_io.out)
+        self.assertIn("WindowsPackageSHA", self.client.out)
+        self.assertIn("PlatformIndependantSHA", self.client.out)
+        self.assertIn("LinuxPackageSHA", self.client.out)
 
     def package_search_nonescaped_characters_test(self):
         self.client.run('search Hello/1.4.10@fenix/testing -q "compiler=gcc AND compiler.libcxx=libstdc++11"')
-        self.assertIn("LinuxPackageSHA", self.client.user_io.out)
-        self.assertNotIn("PlatformIndependantSHA", self.client.user_io.out)
-        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
+        self.assertIn("LinuxPackageSHA", self.client.out)
+        self.assertNotIn("PlatformIndependantSHA", self.client.out)
+        self.assertNotIn("WindowsPackageSHA", self.client.out)
 
         self.client.run('search Hello/1.4.10@fenix/testing -q "compiler=gcc AND compiler.libcxx=libstdc++"')
-        self.assertNotIn("LinuxPackageSHA", self.client.user_io.out)
-        self.assertIn("PlatformIndependantSHA", self.client.user_io.out)
-        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
+        self.assertNotIn("LinuxPackageSHA", self.client.out)
+        self.assertIn("PlatformIndependantSHA", self.client.out)
+        self.assertNotIn("WindowsPackageSHA", self.client.out)
 
         # Now search with a remote
         os.rmdir(self.servers["local"].paths.store)
         shutil.copytree(self.client.paths.store, self.servers["local"].paths.store)
 
         self.client.run('search Hello/1.4.10@fenix/testing -q "compiler=gcc AND compiler.libcxx=libstdc++11" -r local')
-        self.assertIn("Outdated from recipe: False", self.client.user_io.out)
-        self.assertIn("LinuxPackageSHA", self.client.user_io.out)
-        self.assertNotIn("PlatformIndependantSHA", self.client.user_io.out)
-        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
+        self.assertIn("Outdated from recipe: False", self.client.out)
+        self.assertIn("LinuxPackageSHA", self.client.out)
+        self.assertNotIn("PlatformIndependantSHA", self.client.out)
+        self.assertNotIn("WindowsPackageSHA", self.client.out)
 
         self.client.run('search Hello/1.4.10@fenix/testing -q "compiler=gcc AND compiler.libcxx=libstdc++" -r local')
-        self.assertNotIn("LinuxPackageSHA", self.client.user_io.out)
-        self.assertIn("PlatformIndependantSHA", self.client.user_io.out)
-        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
+        self.assertNotIn("LinuxPackageSHA", self.client.out)
+        self.assertIn("PlatformIndependantSHA", self.client.out)
+        self.assertNotIn("WindowsPackageSHA", self.client.out)
 
         # Now search in all remotes
         os.rmdir(self.servers["search_able"].paths.store)
         shutil.copytree(self.client.paths.store, self.servers["search_able"].paths.store)
 
         self.client.run('search Hello/1.4.10@fenix/testing -q "compiler=gcc AND compiler.libcxx=libstdc++11" -r all')
-        self.assertEqual(str(self.client.user_io.out).count("Outdated from recipe: False"), 2)
-        self.assertEqual(str(self.client.user_io.out).count("LinuxPackageSHA"), 2)
-        self.assertNotIn("PlatformIndependantSHA", self.client.user_io.out)
-        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
+        self.assertEqual(str(self.client.out).count("Outdated from recipe: False"), 2)
+        self.assertEqual(str(self.client.out).count("LinuxPackageSHA"), 2)
+        self.assertNotIn("PlatformIndependantSHA", self.client.out)
+        self.assertNotIn("WindowsPackageSHA", self.client.out)
 
         self.client.run('search Hello/1.4.10@fenix/testing -q "compiler=gcc AND compiler.libcxx=libstdc++" -r all')
-        self.assertNotIn("LinuxPackageSHA", self.client.user_io.out)
-        self.assertEqual(str(self.client.user_io.out).count("PlatformIndependantSHA"), 2)
-        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
+        self.assertNotIn("LinuxPackageSHA", self.client.out)
+        self.assertEqual(str(self.client.out).count("PlatformIndependantSHA"), 2)
+        self.assertNotIn("WindowsPackageSHA", self.client.out)
 
     def _assert_pkg_q(self, query, packages_found, remote):
 
@@ -311,7 +365,7 @@ helloTest/1.4.10@fenix/stable""".format(remote)
         self.client.run(command)
 
         for pack_name in ["LinuxPackageSHA", "PlatformIndependantSHA", "WindowsPackageSHA"]:
-            self.assertEquals(pack_name in self.client.user_io.out,
+            self.assertEquals(pack_name in self.client.out,
                               pack_name in packages_found, "%s fail" % pack_name)
 
     def package_search_complex_queries_test(self):
@@ -382,61 +436,64 @@ helloTest/1.4.10@fenix/stable""".format(remote)
         shutil.copytree(self.client.paths.store, self.servers["search_able"].paths.store)
 
         self.client.run("search Hello/1.4.10/fenix/testing -r=all")
-        self.assertIn("Existing recipe in remote 'local':", self.client.user_io.out)
-        self.assertIn("Existing recipe in remote 'search_able':", self.client.user_io.out)
+        self.assertIn("Existing recipe in remote 'local':", self.client.out)
+        self.assertIn("Existing recipe in remote 'search_able':", self.client.out)
 
-        self.assertEqual(str(self.client.user_io.out).count("WindowsPackageSHA"), 2)
-        self.assertEqual(str(self.client.user_io.out).count("PlatformIndependantSHA"), 2)
-        self.assertEqual(str(self.client.user_io.out).count("LinuxPackageSHA"), 2)
+        self.assertEqual(str(self.client.out).count("WindowsPackageSHA"), 2)
+        self.assertEqual(str(self.client.out).count("PlatformIndependantSHA"), 2)
+        self.assertEqual(str(self.client.out).count("LinuxPackageSHA"), 2)
 
     def package_search_with_invalid_query_test(self):
         self.client.run("search Hello/1.4.10/fenix/testing -q 'invalid'", ignore_error=True)
-        self.assertIn("Invalid package query: invalid", self.client.user_io.out)
+        self.assertIn("Invalid package query: invalid", self.client.out)
 
         self.client.run("search Hello/1.4.10/fenix/testing -q 'os= 3'", ignore_error=True)
-        self.assertIn("Invalid package query: os= 3", self.client.user_io.out)
+        self.assertIn("Invalid package query: os= 3", self.client.out)
 
         self.client.run("search Hello/1.4.10/fenix/testing -q 'os=3 FAKE '", ignore_error=True)
-        self.assertIn("Invalid package query: os=3 FAKE ", self.client.user_io.out)
+        self.assertIn("Invalid package query: os=3 FAKE ", self.client.out)
 
         self.client.run("search Hello/1.4.10/fenix/testing -q 'os=3 os.compiler=4'", ignore_error=True)
-        self.assertIn("Invalid package query: os=3 os.compiler=4", self.client.user_io.out)
+        self.assertIn("Invalid package query: os=3 os.compiler=4", self.client.out)
 
         self.client.run("search Hello/1.4.10/fenix/testing -q 'not os=3 AND os.compiler=4'", ignore_error=True)
-        self.assertIn("Invalid package query: not os=3 AND os.compiler=4. 'not' operator is not allowed", self.client.user_io.out)
+        self.assertIn("Invalid package query: not os=3 AND os.compiler=4. 'not' operator is not allowed",
+                      self.client.out)
 
         self.client.run("search Hello/1.4.10/fenix/testing -q 'os=3 AND !os.compiler=4'", ignore_error=True)
-        self.assertIn("Invalid package query: os=3 AND !os.compiler=4. '!' character is not allowed", self.client.user_io.out)
+        self.assertIn("Invalid package query: os=3 AND !os.compiler=4. '!' character is not allowed", self.client.out)
 
     def package_search_properties_filter_test(self):
 
         # All packages without filter
         self.client.run("search Hello/1.4.10/fenix/testing -q ''")
 
-        self.assertIn("WindowsPackageSHA", self.client.user_io.out)
-        self.assertIn("PlatformIndependantSHA", self.client.user_io.out)
-        self.assertIn("LinuxPackageSHA", self.client.user_io.out)
+        self.assertIn("WindowsPackageSHA", self.client.out)
+        self.assertIn("PlatformIndependantSHA", self.client.out)
+        self.assertIn("LinuxPackageSHA", self.client.out)
 
         self.client.run('search Hello/1.4.10/fenix/testing -q os=Windows')
-        self.assertIn("WindowsPackageSHA", self.client.user_io.out)
-        self.assertIn("PlatformIndependantSHA", self.client.user_io.out)
-        self.assertNotIn("LinuxPackageSHA", self.client.user_io.out)
+        self.assertIn("WindowsPackageSHA", self.client.out)
+        self.assertIn("PlatformIndependantSHA", self.client.out)
+        self.assertNotIn("LinuxPackageSHA", self.client.out)
 
         self.client.run('search Hello/1.4.10/fenix/testing -q "os=Windows AND compiler.version=4.5"')
-        self.assertIn("There are no packages for reference 'Hello/1.4.10@fenix/testing' matching the query 'os=Windows AND compiler.version=4.5'", self.client.user_io.out)
+        self.assertIn("There are no packages for reference 'Hello/1.4.10@fenix/testing' "
+                      "matching the query 'os=Windows AND compiler.version=4.5'", self.client.out)
 
         self.client.run('search Hello/1.4.10/fenix/testing -q "os=Linux AND compiler.version=4.5"')
-        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
-        self.assertNotIn("PlatformIndependantSHA", self.client.user_io.out)
-        self.assertIn("LinuxPackageSHA", self.client.user_io.out)
+        self.assertNotIn("WindowsPackageSHA", self.client.out)
+        self.assertNotIn("PlatformIndependantSHA", self.client.out)
+        self.assertIn("LinuxPackageSHA", self.client.out)
 
         self.client.run('search Hello/1.4.10/fenix/testing -q "compiler.version=1.0"')
-        self.assertIn("There are no packages for reference 'Hello/1.4.10@fenix/testing' matching the query 'compiler.version=1.0'", self.client.user_io.out)
+        self.assertIn("There are no packages for reference 'Hello/1.4.10@fenix/testing' "
+                      "matching the query 'compiler.version=1.0'", self.client.out)
 
         self.client.run('search Hello/1.4.10/fenix/testing -q "compiler=gcc AND compiler.version=4.5"')
-        self.assertNotIn("WindowsPackageSHA", self.client.user_io.out)
-        self.assertNotIn("PlatformIndependantSHA", self.client.user_io.out)
-        self.assertIn("LinuxPackageSHA", self.client.user_io.out)
+        self.assertNotIn("WindowsPackageSHA", self.client.out)
+        self.assertNotIn("PlatformIndependantSHA", self.client.out)
+        self.assertIn("LinuxPackageSHA", self.client.out)
 
         self.client.run('search Hello/1.4.10/fenix/testing -q "arch=x86"')
         # One package will be outdated from recipe and another don't
@@ -467,21 +524,21 @@ helloTest/1.4.10@fenix/stable""".format(remote)
             compiler.version: 4.3
         Outdated from recipe: True
 
-""", self.client.user_io.out)
+""", self.client.out)
 
         self.client.run('search helloTest/1.4.10@fenix/stable -q use_OpenGL=False')
         self.assertIn("There are no packages for reference 'helloTest/1.4.10@fenix/stable' "
-                      "matching the query 'use_OpenGL=False'", self.client.user_io.out)
+                      "matching the query 'use_OpenGL=False'", self.client.out)
 
         self.client.run('search helloTest/1.4.10@fenix/stable -q use_OpenGL=True')
-        self.assertIn("Existing packages for recipe helloTest/1.4.10@fenix/stable", self.client.user_io.out)
+        self.assertIn("Existing packages for recipe helloTest/1.4.10@fenix/stable", self.client.out)
 
         self.client.run('search helloTest/1.4.10@fenix/stable -q "use_OpenGL=True AND arch=x64"')
-        self.assertIn("Existing packages for recipe helloTest/1.4.10@fenix/stable", self.client.user_io.out)
+        self.assertIn("Existing packages for recipe helloTest/1.4.10@fenix/stable", self.client.out)
 
         self.client.run('search helloTest/1.4.10@fenix/stable -q "use_OpenGL=True AND arch=x86"')
         self.assertIn("There are no packages for reference 'helloTest/1.4.10@fenix/stable' "
-                      "matching the query 'use_OpenGL=True AND arch=x86'", self.client.user_io.out)
+                      "matching the query 'use_OpenGL=True AND arch=x86'", self.client.out)
 
     def search_with_no_local_test(self):
         client = TestClient()
@@ -907,6 +964,7 @@ helloTest/1.4.10@fenix/stable""".format(remote)
         self.assertIn("WARN: Remotes registry file missing, creating default one", client.out)
         self.assertIn("There are no packages matching the 'my_pkg' pattern", client.out)
 
+
 class SearchOutdatedTest(unittest.TestCase):
     def search_outdated_test(self):
         test_server = TestServer(users={"lasote": "password"})  # exported users and passwords
diff --git a/conans/test/functional/proxies_conf_test.py b/conans/test/functional/proxies_conf_test.py
index c1b27c56d..c1ab870ae 100644
--- a/conans/test/functional/proxies_conf_test.py
+++ b/conans/test/functional/proxies_conf_test.py
@@ -1,6 +1,7 @@
 import unittest
 import os
 
+from conans import tools
 from conans.test.utils.tools import TestClient
 from conans.util.files import save
 from conans.client.conan_api import get_basic_requester
@@ -59,3 +60,44 @@ http=http://conan.url
         self.assertEquals(client.requester.get("**otherexcluded_one***"), "excluded!")
         self.assertEquals(client.requester.get("MyExcludedUrl***"), "excluded!")
         self.assertEquals(client.requester.get("**MyExcludedUrl***"), "not excluded!")
+
+    def test_environ_kept(self):
+        client = TestClient()
+        conf = """
+[proxies]
+        """
+        save(client.client_cache.conan_conf_path, conf)
+        client.client_cache.invalidate()
+        requester = get_basic_requester(client.client_cache)
+
+        def verify_env(url, **kwargs):
+            self.assertTrue("HTTP_PROXY" in os.environ)
+
+        with tools.environment_append({"HTTP_PROXY": "my_system_proxy"}):
+            requester._requester.get = verify_env
+            requester.get("MyUrl")
+
+    def test_environ_removed(self):
+
+        client = TestClient()
+        conf = """
+[proxies]
+no_proxy_match=MyExcludedUrl*
+"""
+        save(client.client_cache.conan_conf_path, conf)
+        client.client_cache.invalidate()
+        requester = get_basic_requester(client.client_cache)
+
+        def verify_env(url, **kwargs):
+            self.assertFalse("HTTP_PROXY" in os.environ)
+            self.assertFalse("http_proxy" in os.environ)
+
+        with tools.environment_append({"http_proxy": "my_system_proxy"}):
+            requester._requester.get = verify_env
+            requester.get("MyUrl")
+            self.assertEqual(os.environ["http_proxy"], "my_system_proxy")
+
+        with tools.environment_append({"HTTP_PROXY": "my_system_proxy"}):
+            requester._requester.get = verify_env
+            requester.get("MyUrl")
+            self.assertEqual(os.environ["HTTP_PROXY"], "my_system_proxy")
diff --git a/conans/test/functional/scm_test.py b/conans/test/functional/scm_test.py
index 2420e5b44..e510fce5c 100644
--- a/conans/test/functional/scm_test.py
+++ b/conans/test/functional/scm_test.py
@@ -79,6 +79,23 @@ class ConanLib(ConanFile):
         folder = self.client.client_cache.source(ConanFileReference.loads("lib/0.1@user/channel"))
         self.assertIn("othersubfolder", os.listdir(folder))
 
+    def test_auto_filesystem_remote_git(self):
+        # https://github.com/conan-io/conan/issues/3109
+        conanfile = base.format(directory="None", url="auto", revision="auto")
+        repo = temp_folder()
+        self.client.save({"conanfile.py": conanfile, "myfile.txt": "My file is copied"}, repo)
+        self.client.runner("git init .", cwd=repo)
+        self.client.runner('git config user.email "you@example.com"', cwd=repo)
+        self.client.runner('git config user.name "Your Name"', cwd=repo)
+        self.client.runner("git add .", cwd=repo)
+        self.client.runner('git commit -m  "commiting"', cwd=repo)
+        self.client.runner('git clone "%s" .' % repo, cwd=self.client.current_folder)
+        self.client.run("export . user/channel")
+        self.assertIn("WARN: Repo origin looks like a local path", self.client.out)
+        os.remove(self.client.client_cache.scm_folder(ConanFileReference.loads("lib/0.1@user/channel")))
+        self.client.run("install lib/0.1@user/channel --build")
+        self.assertIn("lib/0.1@user/channel: Getting sources from url:", self.client.out)
+
     def test_auto_git(self):
         curdir = self.client.current_folder.replace("\\", "/")
         conanfile = base.format(directory="None", url="auto", revision="auto")
diff --git a/conans/test/util/tools_test.py b/conans/test/util/tools_test.py
index fff78866e..eff17920c 100644
--- a/conans/test/util/tools_test.py
+++ b/conans/test/util/tools_test.py
@@ -6,6 +6,9 @@ import platform
 import unittest
 
 from collections import namedtuple
+
+import six
+from mock.mock import patch, mock_open
 from six import StringIO
 
 from conans.client.client_cache import CONAN_CONF
@@ -14,7 +17,7 @@ from conans import tools
 from conans.client.conan_api import ConanAPIV1
 from conans.client.conf import default_settings_yml, default_client_conf
 from conans.client.output import ConanOutput
-from conans.client.tools.win import vcvars_dict
+from conans.client.tools.win import vcvars_dict, vswhere
 from conans.client.tools.scm import Git
 
 from conans.errors import ConanException, NotFoundException
@@ -516,9 +519,8 @@ class HelloConan(ConanFile):
         # This package hopefully doesn't exist
         self.assertFalse(spt._tool.installed("oidfjgesiouhrgioeurhgielurhgaeiorhgioearhgoaeirhg"))
 
+    @unittest.skipUnless(platform.system() == "Windows", "Requires vswhere")
     def msvc_build_command_test(self):
-        if platform.system() != "Windows":
-            return
         settings = Settings.loads(default_settings_yml)
         settings.os = "Windows"
         settings.compiler = "Visual Studio"
@@ -535,12 +537,77 @@ class HelloConan(ConanFile):
         with self.assertRaisesRegexp(ConanException, "Cannot build_sln_command"):
             tools.msvc_build_command(settings, "project.sln")
 
-        # succesful definition via settings
+        # successful definition via settings
         settings.build_type = "Debug"
         cmd = tools.msvc_build_command(settings, "project.sln")
         self.assertIn('msbuild project.sln /p:Configuration=Debug /p:Platform="x86"', cmd)
         self.assertIn('vcvarsall.bat', cmd)
 
+    @unittest.skipUnless(platform.system() == "Windows", "Requires vswhere")
+    def vswhere_description_strip_test(self):
+        myoutput = """
+[
+  {
+    "instanceId": "17609d7c",
+    "installDate": "2018-06-11T02:15:04Z",
+    "installationName": "VisualStudio/15.7.3+27703.2026",
+    "installationPath": "",
+    "installationVersion": "15.7.27703.2026",
+    "productId": "Microsoft.VisualStudio.Product.Enterprise",
+    "productPath": "",
+    "isPrerelease": false,
+    "displayName": "Visual Studio Enterprise 2017",
+    "description": "生産性向上と、さまざまな規模のチーム間の調整のための Microsoft DevOps ソリューション",
+    "channelId": "VisualStudio.15.Release",
+    "channelUri": "https://aka.ms/vs/15/release/channel",
+    "enginePath": "",
+    "releaseNotes": "https://go.microsoft.com/fwlink/?LinkId=660692#15.7.3",
+    "thirdPartyNotices": "https://go.microsoft.com/fwlink/?LinkId=660708",
+    "updateDate": "2018-06-11T02:15:04.7009868Z",
+    "catalog": {
+      "buildBranch": "d15.7",
+      "buildVersion": "15.7.27703.2026",
+      "id": "VisualStudio/15.7.3+27703.2026",
+      "localBuild": "build-lab",
+      "manifestName": "VisualStudio",
+      "manifestType": "installer",
+      "productDisplayVersion": "15.7.3",
+      "productLine": "Dev15",
+      "productLineVersion": "2017",
+      "productMilestone": "RTW",
+      "productMilestoneIsPreRelease": "False",
+      "productName": "Visual Studio",
+      "productPatchVersion": "3",
+      "productPreReleaseMilestoneSuffix": "1.0",
+      "productRelease": "RTW",
+      "productSemanticVersion": "15.7.3+27703.2026",
+      "requiredEngineVersion": "1.16.1187.57215"
+    },
+    "properties": {
+      "campaignId": "",
+      "canceled": "0",
+      "channelManifestId": "VisualStudio.15.Release/15.7.3+27703.2026",
+      "nickname": "",
+      "setupEngineFilePath": ""
+    }
+  },
+  {
+    "instanceId": "VisualStudio.12.0",
+    "installationPath": "",
+    "installationVersion": "12.0"
+  }
+]
+
+"""
+        if six.PY3:
+            # In python3 the output from subprocess.check_output are bytes, not str
+            myoutput = myoutput.encode()
+        myrunner = mock_open()
+        myrunner.check_output = lambda x: myoutput
+        with patch('conans.client.tools.win.subprocess', myrunner):
+            json = vswhere()
+            self.assertNotIn("descripton", json)
+
     def vcvars_echo_test(self):
         if platform.system() != "Windows":
             return