Repository: pypa/build (python)

Issue:
v0.6 is not compatible with python 3.6 due to use of `stacklevel` in log calls
# Problem

V0.6 fails on python 3.6 with 

<details>
  <summary>
TypeError: _log() got an unexpected keyword argument 'stacklevel'
</summary>
 
``` python
/home/jenkins/workspace/_core_dependabot_pip_build-0.6.0/project/inmanta-core/.tox/py36/lib/python3.6/site-packages/build/env.py:100: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

message = 'Creating virtualenv isolated environment...'

    @staticmethod
    def log(message: str) -> None:
        """
        Prints message
    
        The default implementation uses the logging module but this function can be
        overwritten by users to have a different implementation.
    
        :param msg: Message to output
        """
>       _logger.log(logging.INFO, message, stacklevel=2)

/home/jenkins/workspace/_core_dependabot_pip_build-0.6.0/project/inmanta-core/.tox/py36/lib/python3.6/site-packages/build/env.py:138: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Logger build.env (DEBUG)>, level = 20
msg = 'Creating virtualenv isolated environment...', args = ()
kwargs = {'stacklevel': 2}

    def log(self, level, msg, *args, **kwargs):
        """
        Log 'msg % args' with the integer severity 'level'.
    
        To pass exception information, use the keyword argument exc_info with
        a true value, e.g.
    
        logger.log(level, "We have a %s", "mysterious problem", exc_info=1)
        """
        if not isinstance(level, int):
            if raiseExceptions:
                raise TypeError("level must be an integer")
            else:
                return
        if self.isEnabledFor(level):
>           self._log(level, msg, args, **kwargs)
E           TypeError: _log() got an unexpected keyword argument 'stacklevel'
```
</details>

# Cause 

this change https://github.com/pypa/build/commit/32398749d06b19bd2cf21be278e8ceb871411dbb#diff-4f1bb46d65de7fbb1573dfc2a2bca9f63c7b03d55e74c53339f0c8ee4fa054d8R129
makes use of the stacklevel argument, which is only supported from python 3.8
[ref](https://docs.python.org/3/library/logging.html#logging.Logger.debug)

Pull request description:
build: restore compatibility with Python 3.6 and 3.7
fixes #338 

Patch:
diff --git a/src/build/__init__.py b/src/build/__init__.py
index 4b0b0e7..beb3469 100644
--- a/src/build/__init__.py
+++ b/src/build/__init__.py
@@ -441,7 +441,10 @@ class ProjectBuilder:
 
         :param msg: Message to output
         """
-        _logger.log(logging.INFO, message, stacklevel=2)
+        if sys.version_info >= (3, 8):
+            _logger.log(logging.INFO, message, stacklevel=2)
+        else:
+            _logger.log(logging.INFO, message)
 
 
 __all__ = (
diff --git a/src/build/env.py b/src/build/env.py
index c37ff8e..d9d8077 100644
--- a/src/build/env.py
+++ b/src/build/env.py
@@ -135,7 +135,10 @@ class IsolatedEnvBuilder:
 
         :param msg: Message to output
         """
-        _logger.log(logging.INFO, message, stacklevel=2)
+        if sys.version_info >= (3, 8):
+            _logger.log(logging.INFO, message, stacklevel=2)
+        else:
+            _logger.log(logging.INFO, message)
 
 
 class _IsolatedEnvVenvPip(IsolatedEnv):


Test patch:
diff --git a/tests/test_env.py b/tests/test_env.py
index 37acbca..f7be8aa 100644
--- a/tests/test_env.py
+++ b/tests/test_env.py
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: MIT
 import collections
+import logging
 import os
 import platform
 import shutil
@@ -98,6 +99,24 @@ def test_isolated_env_has_install_still_abstract():
         Env()
 
 
+def test_isolated_env_log(mocker, caplog, test_flit_path):
+    mocker.patch('build.env._subprocess')
+    caplog.set_level(logging.DEBUG)
+
+    builder = build.env.IsolatedEnvBuilder()
+    builder.log('something')
+    with builder as env:
+        env.install(['something'])
+
+    assert [(record.levelname, record.message) for record in caplog.records] == [
+        ('INFO', 'something'),
+        ('INFO', 'Creating venv isolated environment...'),
+        ('INFO', 'Installing packages in isolated environment... (something)'),
+    ]
+    if sys.version_info >= (3, 8):  # stacklevel
+        assert [(record.lineno) for record in caplog.records] == [107, 103, 194]
+
+
 @pytest.mark.isolated
 def test_default_pip_is_never_too_old():
     with build.env.IsolatedEnvBuilder() as env:
diff --git a/tests/test_projectbuilder.py b/tests/test_projectbuilder.py
index 4d58f33..c61871e 100644
--- a/tests/test_projectbuilder.py
+++ b/tests/test_projectbuilder.py
@@ -3,6 +3,7 @@
 
 import copy
 import importlib
+import logging
 import os
 import sys
 import textwrap
@@ -545,3 +546,28 @@ def test_toml_instead_of_tomli(mocker, mock_tomli_not_available, tmp_dir, test_f
     builder.build('sdist', '.')
 
     builder._hook.build_sdist.assert_called_with(os.path.abspath('.'), None)
+
+
+def test_log(mocker, caplog, test_flit_path):
+    mocker.patch('pep517.wrappers.Pep517HookCaller', autospec=True)
+    mocker.patch('build.ProjectBuilder._call_backend', return_value='some_path')
+    caplog.set_level(logging.DEBUG)
+
+    builder = build.ProjectBuilder(test_flit_path)
+    builder.get_requires_for_build('sdist')
+    builder.get_requires_for_build('wheel')
+    builder.prepare('wheel', '.')
+    builder.build('sdist', '.')
+    builder.build('wheel', '.')
+    builder.log('something')
+
+    assert [(record.levelname, record.message) for record in caplog.records] == [
+        ('INFO', 'Getting dependencies for sdist...'),
+        ('INFO', 'Getting dependencies for wheel...'),
+        ('INFO', 'Getting metadata for wheel...'),
+        ('INFO', 'Building sdist...'),
+        ('INFO', 'Building wheel...'),
+        ('INFO', 'something'),
+    ]
+    if sys.version_info >= (3, 8):  # stacklevel
+        assert [(record.lineno) for record in caplog.records] == [305, 305, 338, 368, 368, 562]