From 8e584055cf1b7dcfa7497fc1a5c1a937199428b7 Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Wed, 3 Apr 2019 10:11:44 +0700 Subject: [PATCH 1/8] conftest.py: Ignore KeyboardInterrupt During check_internet() and check_github_ssh(), a KeyboardInterrupt should be interpreted as user desire to escape the check, not escape the entire test run. This is especially true during check_github_ssh which may require a passphrase from the user, which they might feel uncomfortable giving during a test suite. After these checks are bypassed, there is user feedback indicating the tests are running, and so they can trigger KeyboardInterrupt again if they wish to escape the entire test run. --- tests/integration/conftest.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 766291e8..d6d0aded 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -38,6 +38,10 @@ def check_internet(): for url in ("http://httpbin.org/ip", "http://clients3.google.com/generate_204"): try: try_internet(url) + except KeyboardInterrupt: + warnings.warn( + "Skipped connecting to internet: {0}".format(url), RuntimeWarning + ) except Exception: warnings.warn( "Failed connecting to internet: {0}".format(url), RuntimeWarning @@ -58,6 +62,10 @@ def check_github_ssh(): # return_code=255 and say 'Permission denied (publickey).' c = delegator.run('ssh -T git@github.com') res = True if c.return_code == 1 else False + except KeyboardInterrupt: + warnings.warn( + "KeyboardInterrupt while checking GitHub ssh access", RuntimeWarning + ) except Exception: pass global HAS_WARNED_GITHUB From 9feb0e08ec3233d6f99c807f0ae0418533b0ca27 Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Wed, 3 Apr 2019 12:08:30 +0700 Subject: [PATCH 2/8] Add news entry --- news/3669.trivial.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/3669.trivial.rst diff --git a/news/3669.trivial.rst b/news/3669.trivial.rst new file mode 100644 index 00000000..86ff9280 --- /dev/null +++ b/news/3669.trivial.rst @@ -0,0 +1 @@ +Allow KeyboardInterrupt to cancel test suite checks for working internet and ssh From 2481471b7dc15d8633d9b8437f4d53c6ef010167 Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Thu, 4 Apr 2019 09:19:27 +0700 Subject: [PATCH 3/8] Remove unused vendored blindspin This package is no longer used. Closes https://github.com/pypa/pipenv/issues/3640 --- news/3640.trivial.rst | 1 + pipenv/vendor/blindspin/LICENSE | 21 --------- pipenv/vendor/blindspin/__init__.py | 73 ----------------------------- pipenv/vendor/vendor.txt | 1 - tasks/vendoring/__init__.py | 1 - 5 files changed, 1 insertion(+), 96 deletions(-) create mode 100644 news/3640.trivial.rst delete mode 100644 pipenv/vendor/blindspin/LICENSE delete mode 100644 pipenv/vendor/blindspin/__init__.py diff --git a/news/3640.trivial.rst b/news/3640.trivial.rst new file mode 100644 index 00000000..eb9b718d --- /dev/null +++ b/news/3640.trivial.rst @@ -0,0 +1 @@ +Removed unused vendored package blindspin diff --git a/pipenv/vendor/blindspin/LICENSE b/pipenv/vendor/blindspin/LICENSE deleted file mode 100644 index 00bf847d..00000000 --- a/pipenv/vendor/blindspin/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright 2018 Kenneth Reitz - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/pipenv/vendor/blindspin/__init__.py b/pipenv/vendor/blindspin/__init__.py deleted file mode 100644 index a1230e83..00000000 --- a/pipenv/vendor/blindspin/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- - -import sys -import threading -import time -import itertools - - -class Spinner(object): - spinner_cycle = itertools.cycle(u'⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏') - - def __init__(self, beep=False, force=False): - self.beep = beep - self.force = force - self.stop_running = None - self.spin_thread = None - - def start(self): - if sys.stdout.isatty() or self.force: - self.stop_running = threading.Event() - self.spin_thread = threading.Thread(target=self.init_spin) - self.spin_thread.start() - - def stop(self): - if self.spin_thread: - self.stop_running.set() - self.spin_thread.join() - - def init_spin(self): - while not self.stop_running.is_set(): - next_val = next(self.spinner_cycle) - if sys.version_info[0] == 2: - next_val = next_val.encode('utf-8') - sys.stdout.write(next_val) - sys.stdout.flush() - time.sleep(0.07) - sys.stdout.write('\b') - - def __enter__(self): - self.start() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.stop() - if self.beep: - sys.stdout.write('\7') - sys.stdout.flush() - return False - - -def spinner(beep=False, force=False): - """This function creates a context manager that is used to display a - spinner on stdout as long as the context has not exited. - - The spinner is created only if stdout is not redirected, or if the spinner - is forced using the `force` parameter. - - Parameters - ---------- - beep : bool - Beep when spinner finishes. - force : bool - Force creation of spinner even when stdout is redirected. - - Example - ------- - - with spinner(): - do_something() - do_something_else() - - """ - return Spinner(beep, force) diff --git a/pipenv/vendor/vendor.txt b/pipenv/vendor/vendor.txt index f1fe99c0..9eb56415 100644 --- a/pipenv/vendor/vendor.txt +++ b/pipenv/vendor/vendor.txt @@ -1,7 +1,6 @@ appdirs==1.4.3 backports.shutil_get_terminal_size==1.0.0 backports.weakref==1.0.post1 -blindspin==2.0.1 click==7.0 click-completion==0.5.0 click-didyoumean==0.0.3 diff --git a/tasks/vendoring/__init__.py b/tasks/vendoring/__init__.py index dce9e5a7..1d06f489 100644 --- a/tasks/vendoring/__init__.py +++ b/tasks/vendoring/__init__.py @@ -50,7 +50,6 @@ HARDCODED_LICENSE_URLS = { 'delegator.py': 'https://raw.githubusercontent.com/kennethreitz/delegator.py/master/LICENSE', 'click-didyoumean': 'https://raw.githubusercontent.com/click-contrib/click-didyoumean/master/LICENSE', 'click-completion': 'https://raw.githubusercontent.com/click-contrib/click-completion/master/LICENSE', - 'blindspin': 'https://raw.githubusercontent.com/kennethreitz/delegator.py/master/LICENSE', 'shutilwhich': 'https://raw.githubusercontent.com/mbr/shutilwhich/master/LICENSE', 'parse': 'https://raw.githubusercontent.com/techalchemy/parse/master/LICENSE', 'semver': 'https://raw.githubusercontent.com/k-bx/python-semver/master/LICENSE.txt', From 1384a25a38d54ce3041fda297e295d5d07fd932f Mon Sep 17 00:00:00 2001 From: frostming Date: Tue, 14 May 2019 09:54:18 +0800 Subject: [PATCH 4/8] make check unused work --- pipenv/core.py | 6 +++--- tests/integration/test_cli.py | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pipenv/core.py b/pipenv/core.py index 76ce7e8b..9e190343 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -242,7 +242,7 @@ def import_from_code(path="."): rs = [] try: - for r in pipreqs.get_all_imports(path): + for r in pipreqs.get_all_imports(path, encoding="utf-8"): if r not in BAD_PACKAGES: rs.append(r) pkg_names = pipreqs.get_pkg_names(rs) @@ -2534,8 +2534,8 @@ def do_check( if not args: args = [] if unused: - deps_required = [k for k in project.packages.keys()] - deps_needed = import_from_code(unused) + deps_required = [k.lower() for k in project.packages.keys()] + deps_needed = [k.lower() for k in import_from_code(unused)] for dep in deps_needed: try: deps_required.remove(dep) diff --git a/tests/integration/test_cli.py b/tests/integration/test_cli.py index a38883e0..ae1b13dd 100644 --- a/tests/integration/test_cli.py +++ b/tests/integration/test_cli.py @@ -47,7 +47,7 @@ def test_pipenv_site_packages(PipenvInstance): c = p.pipenv('--python python --site-packages') assert c.return_code == 0 assert 'Making site-packages available' in c.err - + # no-global-site-packages.txt under stdlib dir should not exist. c = p.pipenv('run python -c "import sysconfig; print(sysconfig.get_path(\'stdlib\'))"') assert c.return_code == 0 @@ -215,20 +215,21 @@ def test_install_parse_error(PipenvInstance, pypi): @pytest.mark.code @pytest.mark.check @pytest.mark.unused -@pytest.mark.skip(reason="non-deterministic") def test_check_unused(PipenvInstance, pypi): with PipenvInstance(chdir=True, pypi=pypi) as p: with open('__init__.py', 'w') as f: contents = """ import tablib import records +import flask """.strip() f.write(contents) p.pipenv('install requests') p.pipenv('install tablib') - p.pipenv('install records') + p.pipenv('install flask') - assert all(pkg in p.pipfile['packages'] for pkg in ['requests', 'tablib', 'records']) + assert all(pkg in p.pipfile['packages'] for pkg in ['requests', 'tablib', 'flask']) c = p.pipenv('check --unused .') assert 'tablib' not in c.out + assert 'flask' not in c.out From e6b2f6463a905268641158564a23ccef0251586e Mon Sep 17 00:00:00 2001 From: frostming Date: Mon, 20 May 2019 12:45:09 +0800 Subject: [PATCH 5/8] add news entry --- news/3745.bugfix.rst | 1 + tests/integration/test_cli.py | 1 + 2 files changed, 2 insertions(+) create mode 100644 news/3745.bugfix.rst diff --git a/news/3745.bugfix.rst b/news/3745.bugfix.rst new file mode 100644 index 00000000..229047a4 --- /dev/null +++ b/news/3745.bugfix.rst @@ -0,0 +1 @@ +Normalize the package names to lowercase when comparing used and in-Pipfile packages. diff --git a/tests/integration/test_cli.py b/tests/integration/test_cli.py index 0af5bedb..27314b73 100644 --- a/tests/integration/test_cli.py +++ b/tests/integration/test_cli.py @@ -217,6 +217,7 @@ def test_install_parse_error(PipenvInstance, pypi): @pytest.mark.code @pytest.mark.check @pytest.mark.unused +@pytest.mark.needs_internet(reason='required by check') def test_check_unused(PipenvInstance, pypi): with PipenvInstance(chdir=True, pypi=pypi) as p: with open('__init__.py', 'w') as f: From 2dce8355e6ae3df727a35ddf95198de1bcb0a2b1 Mon Sep 17 00:00:00 2001 From: frostming Date: Mon, 20 May 2019 14:28:02 +0800 Subject: [PATCH 6/8] let's see what's happening --- pipenv/core.py | 3 +++ pipenv/vendor/pipreqs/pipreqs.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pipenv/core.py b/pipenv/core.py index 3e62b19c..f90c60d1 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -243,12 +243,15 @@ def import_from_code(path="."): rs = [] try: for r in pipreqs.get_all_imports(path, encoding="utf-8"): + click.echo(r) if r not in BAD_PACKAGES: rs.append(r) pkg_names = pipreqs.get_pkg_names(rs) return [proper_case(r) for r in pkg_names] except Exception: + import traceback + traceback.print_exc() return [] diff --git a/pipenv/vendor/pipreqs/pipreqs.py b/pipenv/vendor/pipreqs/pipreqs.py index 791168a9..c0466adf 100644 --- a/pipenv/vendor/pipreqs/pipreqs.py +++ b/pipenv/vendor/pipreqs/pipreqs.py @@ -68,7 +68,7 @@ def get_all_imports(path, encoding=None, extra_ignore_dirs=None): candidates.append(os.path.basename(root)) files = [fn for fn in files if os.path.splitext(fn)[1] == ".py"] - + print(root, files) candidates += [os.path.splitext(fn)[0] for fn in files] for file_name in files: with open_func(os.path.join(root, file_name), "r", encoding=encoding) as f: From 457a42a69c990f8174c9c0848ed2f1a2b1a2b429 Mon Sep 17 00:00:00 2001 From: frostming Date: Mon, 20 May 2019 16:04:58 +0800 Subject: [PATCH 7/8] exclude venv folders --- tests/integration/test_cli.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_cli.py b/tests/integration/test_cli.py index 27314b73..ae5db176 100644 --- a/tests/integration/test_cli.py +++ b/tests/integration/test_cli.py @@ -220,7 +220,8 @@ def test_install_parse_error(PipenvInstance, pypi): @pytest.mark.needs_internet(reason='required by check') def test_check_unused(PipenvInstance, pypi): with PipenvInstance(chdir=True, pypi=pypi) as p: - with open('__init__.py', 'w') as f: + os.makedirs('mypackage') + with open('mypackage/__init__.py', 'w') as f: contents = """ import tablib import records @@ -233,6 +234,6 @@ import flask assert all(pkg in p.pipfile['packages'] for pkg in ['requests', 'tablib', 'flask']) - c = p.pipenv('check --unused .') + c = p.pipenv('check --unused mypackage') assert 'tablib' not in c.out assert 'flask' not in c.out From bc37beaef43de351a1a215bea1c6fad678774233 Mon Sep 17 00:00:00 2001 From: frostming Date: Mon, 20 May 2019 16:42:18 +0800 Subject: [PATCH 8/8] cleanup --- pipenv/core.py | 7 +++---- pipenv/vendor/pipreqs/pipreqs.py | 2 +- tests/integration/test_cli.py | 9 +++------ 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/pipenv/core.py b/pipenv/core.py index f90c60d1..7f04ac49 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -242,16 +242,15 @@ def import_from_code(path="."): rs = [] try: - for r in pipreqs.get_all_imports(path, encoding="utf-8"): - click.echo(r) + for r in pipreqs.get_all_imports( + path, encoding="utf-8", extra_ignore_dirs=[".venv"] + ): if r not in BAD_PACKAGES: rs.append(r) pkg_names = pipreqs.get_pkg_names(rs) return [proper_case(r) for r in pkg_names] except Exception: - import traceback - traceback.print_exc() return [] diff --git a/pipenv/vendor/pipreqs/pipreqs.py b/pipenv/vendor/pipreqs/pipreqs.py index c0466adf..791168a9 100644 --- a/pipenv/vendor/pipreqs/pipreqs.py +++ b/pipenv/vendor/pipreqs/pipreqs.py @@ -68,7 +68,7 @@ def get_all_imports(path, encoding=None, extra_ignore_dirs=None): candidates.append(os.path.basename(root)) files = [fn for fn in files if os.path.splitext(fn)[1] == ".py"] - print(root, files) + candidates += [os.path.splitext(fn)[0] for fn in files] for file_name in files: with open_func(os.path.join(root, file_name), "r", encoding=encoding) as f: diff --git a/tests/integration/test_cli.py b/tests/integration/test_cli.py index ae5db176..f131ae44 100644 --- a/tests/integration/test_cli.py +++ b/tests/integration/test_cli.py @@ -220,20 +220,17 @@ def test_install_parse_error(PipenvInstance, pypi): @pytest.mark.needs_internet(reason='required by check') def test_check_unused(PipenvInstance, pypi): with PipenvInstance(chdir=True, pypi=pypi) as p: - os.makedirs('mypackage') - with open('mypackage/__init__.py', 'w') as f: + with open('__init__.py', 'w') as f: contents = """ import tablib import records import flask """.strip() f.write(contents) - p.pipenv('install requests') - p.pipenv('install tablib') - p.pipenv('install flask') + p.pipenv('install requests tablib flask') assert all(pkg in p.pipfile['packages'] for pkg in ['requests', 'tablib', 'flask']) - c = p.pipenv('check --unused mypackage') + c = p.pipenv('check --unused .') assert 'tablib' not in c.out assert 'flask' not in c.out