Repository: pypa/pipenv

Issue:
JSONDecodeError while locking if pythonwarning is tirggered
### Issue description

With PYTHONWARNINGS=once pipenv fails because warning is appended to JSON generated lock

### Actual result

```
[{"version": "18.2.0", "hashes": ["sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254......, {"version": "4.0.0", "index": "pypi", "hashes": ["sha256:488c842647bbeb350029da10325cb40af0a9c7a2fdda45aeb1dda75b60048ffb", "sha256:c055690dfefa744992f563e8c3a654089a6aa5b8092dded9b6fafbd70b2e45a7"], "name": "pytest"}]/usr/local/lib/python3.6/site-packages/pipenv/patched/notpip/_vendor/msgpack/fallback.py:222: PendingDeprecationWarning: encoding is deprecated, Use raw=False instead.
  PendingDeprecationWarning)


Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/pipenv/utils.py", line 636, in venv_resolve_deps
    results = json.loads(results.split("RESULTS:")[1].strip())
  File "/usr/local/lib/python3.6/json/__init__.py", line 354, in loads
    return _default_decoder.decode(s)
  File "/usr/local/lib/python3.6/json/decoder.py", line 342, in decode
    raise JSONDecodeError("Extra data", s, end)
json.decoder.JSONDecodeError: Extra data: line 1 column 9648 (char 9647)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/pipenv", line 11, in <module>
    sys.exit(cli())
  File "/usr/local/lib/python3.6/site-packages/pipenv/vendor/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/pipenv/vendor/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.6/site-packages/pipenv/vendor/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/local/lib/python3.6/site-packages/pipenv/vendor/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/lib/python3.6/site-packages/pipenv/vendor/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/pipenv/vendor/click/decorators.py", line 64, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/pipenv/vendor/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/pipenv/vendor/click/decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/pipenv/cli/command.py", line 325, in lock
    pypi_mirror=state.pypi_mirror,
  File "/usr/local/lib/python3.6/site-packages/pipenv/core.py", line 1047, in do_lock
    lockfile=lockfile
  File "/usr/local/lib/python3.6/site-packages/pipenv/utils.py", line 649, in venv_resolve_deps
    raise RuntimeError("There was a problem with locking.")
RuntimeError: There was a problem with locking.
```` 

Workaround is set PYTHONWARNINGS=ignore


Discussion:
Hey @Maks3w,

We did a quick check and this issue looks very darn similar to

* [#3271 - PEEP: Allow per-project pipenv configuration](3271)
* [#3270 - emit warning when using VCS dependency without editable flag](3270)
* [#3269 - Latest pythonfinder logic cant handle pyenv and --two combination](3269)

This could be a coincidence, but if any of these issues solves your problem then I did a good job :smile:

If not, the maintainers will get to this issue shortly.

Cheers,
Your Friendly Neighborhood ProBot

Hm the solution is to get warnings to write to stderr...

Resolving patch:
diff --git a/pipenv/utils.py b/pipenv/utils.py
--- a/pipenv/utils.py
+++ b/pipenv/utils.py
@@ -448,6 +448,12 @@ def _should_include_hash(ireq):
             return self.hashes
 
 
+def _show_warning(message, category, filename, lineno, line):
+    warnings.showwarning(message=message, category=category, filename=filename,
+                         lineno=lineno, file=sys.stderr, line=line)
+    sys.stderr.flush()
+
+
 def actually_resolve_deps(
     deps,
     index_lookup,
@@ -462,13 +468,19 @@ def actually_resolve_deps(
 
     if not req_dir:
         req_dir = create_tracked_tempdir(suffix="-requirements", prefix="pipenv-")
-    constraints = get_resolver_metadata(
-        deps, index_lookup, markers_lookup, project, sources,
-    )
-    resolver = Resolver(constraints, req_dir, project, sources, clear=clear, pre=pre)
-    resolved_tree = resolver.resolve()
-    hashes = resolver.resolve_hashes()
+    warning_list = []
+
+    with warnings.catch_warnings(record=True) as warning_list:
+        constraints = get_resolver_metadata(
+            deps, index_lookup, markers_lookup, project, sources,
+        )
+        resolver = Resolver(constraints, req_dir, project, sources, clear=clear, pre=pre)
+        resolved_tree = resolver.resolve()
+        hashes = resolver.resolve_hashes()
 
+    for warning in warning_list:
+        _show_warning(warning.message, warning.category, warning.filename, warning.lineno,
+                      warning.line)
     return (resolved_tree, hashes, markers_lookup, resolver)
 
 


Test patch:
diff --git a/tests/integration/test_lock.py b/tests/integration/test_lock.py
--- a/tests/integration/test_lock.py
+++ b/tests/integration/test_lock.py
@@ -503,3 +503,17 @@ def test_lock_with_incomplete_source(PipenvInstance, pypi):
         c = p.pipenv('install')
         assert c.return_code == 0
         assert p.lockfile['_meta']['sources']
+
+
+@pytest.mark.lock
+@pytest.mark.install
+def test_lock_no_warnings(PipenvInstance, pypi):
+    with PipenvInstance(pypi=pypi, chdir=True) as p:
+        os.environ["PYTHONWARNINGS"] = str("once")
+        c = p.pipenv("install six")
+        assert c.return_code == 0
+        c = p.pipenv('run python -c "import warnings; warnings.warn(\\"This is a warning\\", DeprecationWarning); print(\\"hello\\")"')
+        assert c.return_code == 0
+        assert "Warning" in c.err
+        assert "Warning" not in c.out
+        assert "hello" in c.out