diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..c9efdaea --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +./examples +./tests +./docs +./news +./pipenv +./.git +./.buildkite +./peeps +./.github +./tasks +./.azure-pipelines diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 3dc4613e..e516787f 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -5,7 +5,7 @@ about: Create a report to help us improve Be sure to check the existing issues (both open and closed!), and make sure you are running the latest version of Pipenv. -Check the [diagnose documentation](https://docs.pipenv.org/en/latest/diagnose/) for common issues before posting! We may close your issue if it is very similar to one of them. Please be considerate, or be on your way. +Check the [diagnose documentation](https://pipenv.pypa.io/en/latest/diagnose/) for common issues before posting! We may close your issue if it is very similar to one of them. Please be considerate, or be on your way. Make sure to mention your debugging experience if the documented solution failed. diff --git a/.gitmodules b/.gitmodules index e2f779af..4f0f9fa2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,7 +6,7 @@ url = https://github.com/pinax/pinax.git [submodule "tests/test_artifacts/git/requests"] path = tests/test_artifacts/git/requests - url = https://github.com/kennethreitz/requests.git + url = https://github.com/psf/requests.git [submodule "tests/test_artifacts/git/six"] path = tests/test_artifacts/git/six url = https://github.com/benjaminp/six.git @@ -24,7 +24,7 @@ url = https://github.com/pallets/flask.git [submodule "tests/test_artifacts/git/requests-2.18.4"] path = tests/test_artifacts/git/requests-2.18.4 - url = https://github.com/kennethreitz/requests + url = https://github.com/psf/requests [submodule "tests/pypi"] path = tests/pypi url = https://github.com/sarugaku/pipenv-test-artifacts.git diff --git a/LICENSE b/LICENSE index f5639bb0..ea28562e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright 2017 Kenneth Reitz +Copyright 2020 Python Packaging Authority Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ea2bf074..a8f251db 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ Pipenv: Python Development Workflow for Humans [![image](https://img.shields.io/pypi/l/pipenv.svg)](https://python.org/pypi/pipenv) [![Azure Pipelines Build Status](https://dev.azure.com/pypa/pipenv/_apis/build/status/Pipenv%20CI?branchName=master)](https://dev.azure.com/pypa/pipenv/_build/latest?definitionId=16&branchName=master) [![image](https://img.shields.io/pypi/pyversions/pipenv.svg)](https://python.org/pypi/pipenv) -[![image](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://saythanks.io/to/kennethreitz) ------------------------------------------------------------------------ @@ -18,7 +17,7 @@ well as adds/removes packages from your `Pipfile` as you install/uninstall packages. It also generates the ever-important `Pipfile.lock`, which is used to produce deterministic builds. -![image](https://s3.amazonaws.com/media.kennethreitz.com/pipenv.gif) +![GIF demonstrating Pipenv's usage](https://gist.githubusercontent.com/jlusk/855d611bbcfa2b159839db73d07f6ce9/raw/7f5743401809f7e630ee8ff458faa980e19924a0/pipenv.gif) The problems that Pipenv seeks to solve are multi-faceted: @@ -48,7 +47,7 @@ Or, if you\'re using Debian Buster+: $ sudo apt install pipenv -Or, if you\'re using Fedora 28: +Or, if you\'re using Fedora: $ sudo dnf install pipenv @@ -56,19 +55,13 @@ Or, if you\'re using FreeBSD: # pkg install py36-pipenv -Otherwise, refer to the [documentation](https://docs.pipenv.org/en/latest/install/#installing-pipenv) for instructions. +Otherwise, refer to the [documentation](https://pipenv.pypa.io/en/latest/#install-pipenv-today) for instructions. ✨🍰✨ ☤ User Testimonials ------------------- -**Jannis Leidel**, former pip maintainer--- - -: *Pipenv is the porcelain I always wanted to build for pip. It fits - my brain and mostly replaces virtualenvwrapper and manual pip calls - for me. Use it.* - **David Gang**--- : *This package manager is really awesome. For the first time I know @@ -303,4 +296,4 @@ Use the shell: ☤ Documentation --------------- -Documentation resides over at [pipenv.org](http://pipenv.org/). +Documentation resides over at [pipenv.pypa.io](https://pipenv.pypa.io/en/latest/). diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5f36b459..636ea713 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -40,7 +40,6 @@ jobs: python.architecture: x64 maxParallel: 4 steps: - - template: .azure-pipelines/steps/reinstall-pythons.yml - template: .azure-pipelines/steps/run-tests.yml parameters: vmImage: 'Ubuntu-16.04' @@ -52,7 +51,6 @@ jobs: python.version: '3.7' python.architecture: x64 steps: - - template: .azure-pipelines/steps/reinstall-pythons.yml - template: .azure-pipelines/steps/run-vendor-scripts.yml parameters: vmImage: 'Ubuntu-16.04' @@ -64,7 +62,6 @@ jobs: python.version: '3.7' python.architecture: x64 steps: - - template: .azure-pipelines/steps/reinstall-pythons.yml - template: .azure-pipelines/steps/build-package.yml parameters: vmImage: 'Ubuntu-16.04' diff --git a/docs/_templates/hacks.html b/docs/_templates/hacks.html index 9736d409..fa7d60ed 100644 --- a/docs/_templates/hacks.html +++ b/docs/_templates/hacks.html @@ -59,7 +59,7 @@ - +

Stay Informed

Receive updates on new releases and upcoming projects.

-

Follow @kennethreitz

-

Say Thanks!

-

Join Mailing List.

+

Follow @ThePyPA

+

Join Mailing List.

Other Projects

@@ -47,7 +46,7 @@
  • Pipenv-Pipes
  • -

    More Kenneth Reitz projects:

    +

    More projects founded by Kenneth Reitz:

    @@ -75,4 +74,3 @@
  • 日本語
  • Español
  • - diff --git a/docs/_templates/sidebarlogo.html b/docs/_templates/sidebarlogo.html index 00fb20c4..59e8641e 100644 --- a/docs/_templates/sidebarlogo.html +++ b/docs/_templates/sidebarlogo.html @@ -38,12 +38,11 @@

    Stay Informed

    Receive updates on new releases and upcoming projects.

    -

    -

    Follow @kennethreitz

    -

    Say Thanks!

    -

    Join Mailing List.

    +

    Follow @ThePyPA

    +

    Join Mailing List.

    Other Projects

    @@ -51,7 +50,7 @@
  • Pipenv-Pipes
  • -

    More Kenneth Reitz projects:

    +

    More projects founded by Kenneth Reitz:

    @@ -72,11 +71,10 @@

  • Pipenv @ GitHub
  • -
  • Pipenv @ PyPI
  • +
  • Pipenv @ PyPI
  • Pipenv PPA (PyPA)
  • -
  • Issue Tracker
  • +
  • Issue Tracker

  • 日本語
  • Español
  • - diff --git a/docs/advanced.rst b/docs/advanced.rst index 9efb798e..e6e31f62 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -20,7 +20,7 @@ This document covers some of Pipenv's more glorious and advanced features. If you'd like a specific package to be installed with a specific package index, you can do the following:: [[source]] - url = "https://pypi.python.org/simple" + url = "https://pypi.org/simple" verify_ssl = true name = "pypi" @@ -344,6 +344,21 @@ If a ``.env`` file is present in your project, ``$ pipenv shell`` and ``$ pipenv >>> os.environ['HELLO'] 'WORLD' +Shell like variable expansion is available in ``.env`` files using `${VARNAME}` syntax.:: + + $ cat .env + CONFIG_PATH=${HOME}/.config/foo + + $ pipenv run python + Loading .env environment variables… + Python 3.7.6 (default, Dec 19 2019, 22:52:49) + [GCC 9.2.1 20190827 (Red Hat 9.2.1-1)] on linux + Type "help", "copyright", "credits" or "license" for more information. + >>> import os + >>> os.environ['CONFIG_PATH'] + '/home/kennethreitz/.config/foo' + + This is very useful for keeping production credentials out of your codebase. We do not recommend committing ``.env`` files into source control! @@ -355,6 +370,8 @@ To prevent pipenv from loading the ``.env`` file, set the ``PIPENV_DONT_LOAD_ENV $ PIPENV_DONT_LOAD_ENV=1 pipenv shell +See `theskumar/python-dotenv `_ for more information on ``.env`` files. + ☤ Custom Script Shortcuts ------------------------- @@ -525,7 +542,7 @@ probably a good idea in any case. A 3rd party plugin, `tox-pipenv`_ is also available to use Pipenv natively with tox. -.. _Requests: https://github.com/kennethreitz/requests +.. _Requests: https://github.com/psf/requests .. _tox: https://tox.readthedocs.io/en/latest/ .. _tox-pipenv: https://tox-pipenv.readthedocs.io/en/latest/ .. _Travis-CI: https://travis-ci.org/ diff --git a/docs/basics.rst b/docs/basics.rst index a78df254..fa02fb16 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -10,6 +10,12 @@ This document covers some of Pipenv's more basic features. ☤ Example Pipfile & Pipfile.lock -------------------------------- +Pipfiles contain information for the dependencies of the project, and supercede +the requirements.txt present in Python projects. You should add Pipfile in the +Git repository letting users who clone the repository the only thing required would be +installing Pipenv in the machine and type ``pipenv install``. Pipenv is a reference +implementation for using Pipfile. + .. _example_files: Here is a simple example of a ``Pipfile`` and the resulting ``Pipfile.lock``. @@ -125,7 +131,7 @@ Example Pipfile.lock - Generally, keep both ``Pipfile`` and ``Pipfile.lock`` in version control. - Do not keep ``Pipfile.lock`` in version control if multiple versions of Python are being targeted. -- Specify your target Python version in your `Pipfile`'s ``[requires]`` section. Ideally, you should only have one target Python version, as this is a deployment tool. +- Specify your target Python version in your `Pipfile`'s ``[requires]`` section. Ideally, you should only have one target Python version, as this is a deployment tool. ``python_version`` should be in the format ``X.Y`` and ``python_full_version`` should be in ``X.Y.Z`` format. - ``pipenv install`` is fully compatible with ``pip install`` syntax, for which the full documentation can be found `here `_. - Note that the ``Pipfile`` uses the `TOML Spec `_. @@ -188,7 +194,7 @@ You can specify versions of a package using the `Semantic Versioning scheme +.. _`PEP 440`: https://www.python.org/dev/peps/pep-0440/ To make inclusive or exclusive version comparisons you can use: :: @@ -216,7 +222,7 @@ To avoid installing a specific version you can use the ``!=`` identifier. For an in depth explanation of the valid identifiers and more complex use cases check `the relevant section of PEP-440`_. -.. _`the relevant section of PEP-440`: https://www.python.org/dev/peps/pep-0440/#version-specifiers> +.. _`the relevant section of PEP-440`: https://www.python.org/dev/peps/pep-0440/#version-specifiers ☤ Specifying Versions of Python ------------------------------- @@ -315,6 +321,7 @@ The user can provide these additional parameters: - ``--dev`` — Install both ``develop`` and ``default`` packages from ``Pipfile``. - ``--system`` — Use the system ``pip`` command rather than the one from your virtualenv. + - ``--deploy`` — Make sure the packages are properly locked in Pipfile.lock, and abort if the lock file is out-of-date. - ``--ignore-pipfile`` — Ignore the ``Pipfile`` and install from the ``Pipfile.lock``. - ``--skip-lock`` — Ignore the ``Pipfile.lock`` and install from the ``Pipfile``. In addition, do not write out a ``Pipfile.lock`` reflecting changes to the ``Pipfile``. diff --git a/docs/cli.rst b/docs/cli.rst new file mode 100644 index 00000000..873c67d7 --- /dev/null +++ b/docs/cli.rst @@ -0,0 +1,8 @@ +.. _cli: + +Pipenv CLI Reference +====================================== + +.. click:: pipenv:cli + :prog: pipenv + :show-nested: diff --git a/docs/conf.py b/docs/conf.py index c5d6fbe0..90e16afe 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -57,8 +57,8 @@ master_doc = 'index' # General information about the project. project = u'pipenv' -copyright = u'2017. A Kenneth Reitz Project' -author = u'Kenneth Reitz' +copyright = u'2020. A project founded by Kenneth Reitz' +author = u'Python Packaging Authority' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/dev/philosophy.rst b/docs/dev/philosophy.rst index 9d364e84..3f4c0bd0 100644 --- a/docs/dev/philosophy.rst +++ b/docs/dev/philosophy.rst @@ -7,6 +7,8 @@ Pipenv is an open but opinionated tool, created by an open but opinionated devel Management Style ~~~~~~~~~~~~~~~~ + **To be updated (as of March 2020)**. + `Kenneth Reitz `__ is the BDFL. He has final say in any decision related to the Pipenv project. Kenneth is responsible for the direction and form of the library, as well as its presentation. In addition to making decisions based on technical merit, he is responsible for making decisions based on the development philosophy of Pipenv. `Dan Ryan `__, `Tzu-ping Chung `__, and `Nate Prewitt `__ are the core contributors. diff --git a/docs/index.rst b/docs/index.rst index 5d78ecc7..26bfde19 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,9 +15,6 @@ Pipenv: Python Dev Workflow for Humans .. image:: https://img.shields.io/pypi/pyversions/pipenv.svg :target: https://pypi.python.org/pypi/pipenv -.. image:: https://img.shields.io/badge/Say%20Thanks!-🦉-1EAEDB.svg - :target: https://saythanks.io/to/kennethreitz - --------------- **Pipenv** is a tool that aims to bring the best of all packaging worlds (bundler, composer, npm, cargo, yarn, etc.) to the Python world. *Windows is a first-class citizen, in our world.* @@ -120,6 +117,7 @@ Further Documentation Guides basics advanced + cli diagnose Contribution Guides @@ -131,13 +129,6 @@ Contribution Guides dev/philosophy dev/contributing -☤ Pipenv Usage --------------- - -.. click:: pipenv:cli - :prog: pipenv - :show-nested: - Indices and tables ================== diff --git a/docs/install.rst b/docs/install.rst index b5acd312..6aceba0c 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -146,7 +146,7 @@ To upgrade pipenv at any time:: If you don't even have pip installed, you can use this crude installation method, which will bootstrap your whole system:: - $ curl https://raw.githubusercontent.com/kennethreitz/pipenv/master/get-pipenv.py | python + $ curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python ☤ Installing packages for your project diff --git a/news/3346.doc.rst b/news/3346.doc.rst new file mode 100644 index 00000000..c985f001 --- /dev/null +++ b/news/3346.doc.rst @@ -0,0 +1 @@ +Move CLI docs to its own page. diff --git a/news/3879.bugfix.rst b/news/3879.bugfix.rst new file mode 100644 index 00000000..95413ca5 --- /dev/null +++ b/news/3879.bugfix.rst @@ -0,0 +1 @@ +Pass ``--pre`` and ``--clear`` options to ``pipenv update --outdated``. diff --git a/news/3885.trivial.rst b/news/3885.trivial.rst new file mode 100644 index 00000000..7782e0c9 --- /dev/null +++ b/news/3885.trivial.rst @@ -0,0 +1 @@ +Remove a misleading code comment from Specifying Versions documentation. diff --git a/news/3911.doc.rst b/news/3911.doc.rst new file mode 100644 index 00000000..a5ab134f --- /dev/null +++ b/news/3911.doc.rst @@ -0,0 +1 @@ +Fix link to GIF in README.md demonstrating Pipenv's usage, and add descriptive alt text. diff --git a/news/3912.doc.rst b/news/3912.doc.rst new file mode 100644 index 00000000..24598d19 --- /dev/null +++ b/news/3912.doc.rst @@ -0,0 +1 @@ +Added a line describing potential issues in fancy extension. diff --git a/news/3913.doc.rst b/news/3913.doc.rst new file mode 100644 index 00000000..54fbbfe8 --- /dev/null +++ b/news/3913.doc.rst @@ -0,0 +1 @@ +Documental description of how Pipfile works and association with Pipenv. diff --git a/news/3914.doc.rst b/news/3914.doc.rst new file mode 100644 index 00000000..ae37d61d --- /dev/null +++ b/news/3914.doc.rst @@ -0,0 +1 @@ +Clarify the proper value of `python_version` and `python_full_version`. diff --git a/news/3915.doc.rst b/news/3915.doc.rst new file mode 100644 index 00000000..2cc94a20 --- /dev/null +++ b/news/3915.doc.rst @@ -0,0 +1 @@ +Write description for --deploy extension and few extensions differences. diff --git a/news/4018.feature.rst b/news/4018.feature.rst new file mode 100644 index 00000000..fcd6a2a9 --- /dev/null +++ b/news/4018.feature.rst @@ -0,0 +1 @@ +Added support for automatic python installs via ``asdf`` and associated ``PIPENV_DONT_USE_ASDF`` environment variable. diff --git a/news/4045.bugfix.rst b/news/4045.bugfix.rst new file mode 100644 index 00000000..6558018c --- /dev/null +++ b/news/4045.bugfix.rst @@ -0,0 +1 @@ +Honor PIPENV_SPINNER environment variable diff --git a/news/4100.doc.rst b/news/4100.doc.rst new file mode 100644 index 00000000..050bdcca --- /dev/null +++ b/news/4100.doc.rst @@ -0,0 +1 @@ +More documentation for ``.env`` files diff --git a/news/4137.doc b/news/4137.doc new file mode 100644 index 00000000..45de74d2 --- /dev/null +++ b/news/4137.doc @@ -0,0 +1 @@ +Updated documentation to point to working links. \ No newline at end of file diff --git a/pipenv/__init__.py b/pipenv/__init__.py index 31d49fc1..624397c5 100644 --- a/pipenv/__init__.py +++ b/pipenv/__init__.py @@ -8,7 +8,7 @@ import os import sys import warnings -from .__version__ import __version__ +from .__version__ import __version__ # noqa PIPENV_ROOT = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) @@ -53,7 +53,7 @@ sys.stdout = stdout sys.stderr = stderr from .cli import cli -from . import resolver +from . import resolver # noqa if __name__ == "__main__": cli() diff --git a/pipenv/_compat.py b/pipenv/_compat.py index caba80fe..fdcca7f5 100644 --- a/pipenv/_compat.py +++ b/pipenv/_compat.py @@ -4,8 +4,6 @@ Exposes a standard API that enables compatibility across python versions, operating systems, etc. """ - -import os import sys import warnings @@ -132,7 +130,7 @@ def decode_output(output): output = output.encode(DEFAULT_ENCODING) except (AttributeError, UnicodeDecodeError, UnicodeEncodeError): if six.PY2: - output = unicode.translate(vistir.misc.to_text(output), + output = unicode.translate(vistir.misc.to_text(output), # noqa UNICODE_TO_ASCII_TRANSLATION_MAP) else: output = output.translate(UNICODE_TO_ASCII_TRANSLATION_MAP) @@ -147,5 +145,5 @@ def fix_utf8(text): text = decode_output(text) except UnicodeDecodeError: if six.PY2: - text = unicode.translate(vistir.misc.to_text(text), UNICODE_TO_ASCII_TRANSLATION_MAP) + text = unicode.translate(vistir.misc.to_text(text), UNICODE_TO_ASCII_TRANSLATION_MAP) # noqa return text diff --git a/pipenv/cli/__init__.py b/pipenv/cli/__init__.py index d1819953..0f57e306 100644 --- a/pipenv/cli/__init__.py +++ b/pipenv/cli/__init__.py @@ -1,4 +1,4 @@ # -*- coding=utf-8 -*- from __future__ import absolute_import -from .command import cli +from .command import cli # noqa diff --git a/pipenv/cli/command.py b/pipenv/cli/command.py index c2cd2e49..9d3ce9bf 100644 --- a/pipenv/cli/command.py +++ b/pipenv/cli/command.py @@ -8,17 +8,15 @@ from click import ( argument, echo, edit, group, option, pass_context, secho, version_option ) -from ..vendor import click_completion -from ..vendor import delegator -from ..patched import crayons - from ..__version__ import __version__ +from ..patched import crayons +from ..vendor import click_completion, delegator from .options import ( CONTEXT_SETTINGS, PipenvGroup, code_option, common_options, deploy_option, general_options, install_options, lock_options, pass_state, - pypi_mirror_option, python_option, requirementstxt_option, - skip_lock_option, sync_options, system_option, three_option, - uninstall_options, verbose_option, site_packages_option + pypi_mirror_option, python_option, site_packages_option, skip_lock_option, + sync_options, system_option, three_option, uninstall_options, + verbose_option ) @@ -344,7 +342,8 @@ def lock( "--fancy", is_flag=True, default=False, - help="Run in shell in fancy mode (for elegantly configured shells).", + help="Run in shell in fancy mode. Make sure the shell have no path manipulating" + " scripts. Run $pipenv shell for issues with compatibility mode.", ) @option( "--anyway", @@ -484,7 +483,7 @@ def update( if not outdated: outdated = bool(dry_run) if outdated: - do_outdated(pypi_mirror=state.pypi_mirror) + do_outdated(clear=state.clear, pre=state.installstate.pre, pypi_mirror=state.pypi_mirror) packages = [p for p in state.installstate.packages if p] editable = [p for p in state.installstate.editables if p] if not packages: diff --git a/pipenv/core.py b/pipenv/core.py index 0637c225..74b72359 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -1,49 +1,48 @@ # -*- coding=utf-8 -*- from __future__ import absolute_import, print_function + import io import json as simplejson import logging import os -import shutil import sys import time import warnings import click import six -import urllib3.util as urllib3_util -import vistir -from click_completion import init as init_completion import delegator import dotenv import pipfile +import vistir + +from click_completion import init as init_completion -from .patched import crayons from . import environments, exceptions, pep508checker, progress -from ._compat import fix_utf8, decode_for_output +from ._compat import decode_for_output, fix_utf8 from .cmdparse import Script from .environments import ( - PIPENV_CACHE_DIR, PIPENV_COLORBLIND, PIPENV_DEFAULT_PYTHON_VERSION, - PIPENV_DONT_USE_PYENV, PIPENV_HIDE_EMOJIS, PIPENV_MAX_SUBPROCESS, - PIPENV_PYUP_API_KEY, PIPENV_SHELL_FANCY, PIPENV_SKIP_VALIDATION, - PIPENV_YES, SESSION_IS_INTERACTIVE, PIP_EXISTS_ACTION, PIPENV_RESOLVE_VCS, - is_type_checking + PIP_EXISTS_ACTION, PIPENV_CACHE_DIR, PIPENV_COLORBLIND, + PIPENV_DEFAULT_PYTHON_VERSION, PIPENV_DONT_USE_PYENV, PIPENV_DONT_USE_ASDF, + PIPENV_HIDE_EMOJIS, PIPENV_MAX_SUBPROCESS, PIPENV_PYUP_API_KEY, + PIPENV_RESOLVE_VCS, PIPENV_SHELL_FANCY, PIPENV_SKIP_VALIDATION, PIPENV_YES, + SESSION_IS_INTERACTIVE, is_type_checking ) -from .project import Project, SourceNotFound +from .patched import crayons +from .project import Project from .utils import ( - convert_deps_to_pip, create_mirror_source, create_spinner, download_file, - escape_cmd, escape_grouped_arguments, find_windows_executable, - get_canonical_names, is_pinned, is_pypi_url, is_required_version, is_star, - is_valid_url, parse_indexes, pep423_name, prepare_pip_source_args, - proper_case, python_version, venv_resolve_deps, run_command, - is_python_command, find_python, make_posix, interrupt_handled_subprocess, - get_indexes_from_requirement, get_source_list, get_project_index, + convert_deps_to_pip, create_spinner, download_file, + escape_grouped_arguments, find_python, find_windows_executable, + get_canonical_names, get_source_list, interrupt_handled_subprocess, + is_pinned, is_python_command, is_required_version, is_star, is_valid_url, + parse_indexes, pep423_name, prepare_pip_source_args, proper_case, + python_version, run_command, venv_resolve_deps ) if is_type_checking(): - from typing import Dict, List, Mapping, Optional, Union, Text + from typing import Dict, List, Optional, Union, Text from pipenv.vendor.requirementslib.models.requirements import Requirement TSourceDict = Dict[Text, Union[Text, bool]] @@ -393,28 +392,36 @@ def ensure_python(three=None, python=None): ), err=True, ) - # Pyenv is installed - from .vendor.pythonfinder.environment import PYENV_INSTALLED + # check for python installers + from .vendor.pythonfinder.environment import PYENV_INSTALLED, ASDF_INSTALLED + from .installers import Pyenv, Asdf, InstallerError - if not PYENV_INSTALLED: + # prefer pyenv if both pyenv and asdf are installed as it's + # dedicated to python installs so probably the preferred + # method of the user for new python installs. + if PYENV_INSTALLED and not PIPENV_DONT_USE_PYENV: + installer = Pyenv("pyenv") + elif ASDF_INSTALLED and not PIPENV_DONT_USE_ASDF: + installer = Asdf("asdf") + else: + installer = None + + if not installer: abort() else: - if (not PIPENV_DONT_USE_PYENV) and (SESSION_IS_INTERACTIVE or PIPENV_YES): - from .pyenv import Runner, PyenvError - - pyenv = Runner("pyenv") + if SESSION_IS_INTERACTIVE or PIPENV_YES: try: - version = pyenv.find_version_to_install(python) + version = installer.find_version_to_install(python) except ValueError: abort() - except PyenvError as e: + except InstallerError as e: click.echo(fix_utf8("Something went wrong…")) click.echo(crayons.blue(e.err), err=True) abort() s = "{0} {1} {2}".format( "Would you like us to install", crayons.green("CPython {0}".format(version)), - "with pyenv?", + "with {0}?".format(installer), ) # Prompt the user to continue… if not (PIPENV_YES or click.confirm(s, default=True)): @@ -425,15 +432,15 @@ def ensure_python(three=None, python=None): u"{0} {1} {2} {3}{4}".format( crayons.normal(u"Installing", bold=True), crayons.green(u"CPython {0}".format(version), bold=True), - crayons.normal(u"with pyenv", bold=True), + crayons.normal(u"with {0}".format(installer), bold=True), crayons.normal(u"(this may take a few minutes)"), crayons.normal(fix_utf8("…"), bold=True), ) ) with create_spinner("Installing python...") as sp: try: - c = pyenv.install(version) - except PyenvError as e: + c = installer.install(version) + except InstallerError as e: sp.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format( "Failed...") ) @@ -711,8 +718,6 @@ def batch_install(deps_list, procs, failed_deps_queue, label=label ) - - indexes = [] trusted_hosts = [] # Install these because for dep in deps_list_bar: @@ -723,9 +728,6 @@ def batch_install(deps_list, procs, failed_deps_queue, dep.markers = str(strip_extras_markers_from_requirement(dep.get_markers())) # Install the module. is_artifact = False - if no_deps: - link = getattr(dep.req, "link", None) - is_wheel = getattr(link, "is_wheel", False) if link else False if dep.is_file_or_url and (dep.is_direct_url or any( dep.req.uri.endswith(ext) for ext in ["zip", "tar.gz"] )): @@ -1053,7 +1055,6 @@ def do_lock( # Resolve dev-package dependencies, with pip-tools. for is_dev in [True, False]: pipfile_section = "dev-packages" if is_dev else "packages" - lockfile_section = "develop" if is_dev else "default" if project.pipfile_exists: packages = project.parsed_pipfile.get(pipfile_section, {}) else: @@ -1355,7 +1356,7 @@ def get_requirement_line( return ["-e", line] return '-e {0}'.format(line) if not format_for_file: - return [line,] + return [line] return line return requirement.as_line(include_hashes=include_hashes, as_list=not format_for_file) @@ -1767,7 +1768,7 @@ def do_py(system=False): click.echo(crayons.red("No project found!")) -def do_outdated(pypi_mirror=None): +def do_outdated(pypi_mirror=None, pre=False, clear=False): # TODO: Allow --skip-lock here? from .vendor.requirementslib.models.requirements import Requirement from .vendor.requirementslib.models.utils import get_version @@ -1789,7 +1790,7 @@ def do_outdated(pypi_mirror=None): dep = Requirement.from_line(str(result.as_requirement())) packages.update(dep.as_pipfile()) updated_packages = {} - lockfile = do_lock(write=False, pypi_mirror=pypi_mirror) + lockfile = do_lock(clear=clear, pre=pre, write=False, pypi_mirror=pypi_mirror) for section in ("develop", "default"): for package in lockfile[section]: try: diff --git a/pipenv/environment.py b/pipenv/environment.py index 4a694019..b57a858a 100644 --- a/pipenv/environment.py +++ b/pipenv/environment.py @@ -10,7 +10,6 @@ import os import site import sys -from distutils.sysconfig import get_python_lib from sysconfig import get_paths, get_python_version import itertools @@ -620,7 +619,7 @@ class Environment(object): else: d['required_version'] = d['installed_version'] - get_children = lambda n: key_tree.get(n.key, []) + get_children = lambda n: key_tree.get(n.key, []) # noqa d['dependencies'] = [ cls._get_requirements_for_package(c, key_tree, parent=node, @@ -805,7 +804,7 @@ class Environment(object): sys.path = self.sys_path sys.prefix = self.sys_prefix site.addsitedir(self.base_paths["purelib"]) - pip = self.safe_import("pip") + pip = self.safe_import("pip") # noqa pip_vendor = self.safe_import("pip._vendor") pep517_dir = os.path.join(os.path.dirname(pip_vendor.__file__), "pep517") site.addsitedir(pep517_dir) @@ -865,7 +864,7 @@ class Environment(object): def install(self, requirements): if not isinstance(requirements, (tuple, list)): - requirements = [requirements,] + requirements = [requirements] with self.get_finder() as finder: args = [] for format_control in ('no_binary', 'only_binary'): @@ -917,7 +916,7 @@ class Environment(object): pathset_base = pip_shims.UninstallPathSet pathset_base._permitted = PatchedUninstaller._permitted dist = next( - iter(filter(lambda d: d.project_name == pkgname, self.get_working_set())), + iter(d for d in self.get_working_set() if d.project_name == pkgname), None ) pathset = pathset_base.from_dist(dist) @@ -925,7 +924,7 @@ class Environment(object): pathset.remove(auto_confirm=auto_confirm, verbose=verbose) try: yield pathset - except Exception as e: + except Exception: if pathset is not None: pathset.rollback() else: diff --git a/pipenv/environments.py b/pipenv/environments.py index e59ed63a..622b76ff 100644 --- a/pipenv/environments.py +++ b/pipenv/environments.py @@ -68,6 +68,12 @@ PIPENV_DONT_USE_PYENV = bool(os.environ.get("PIPENV_DONT_USE_PYENV")) Default is to install Python automatically via pyenv when needed, if possible. """ +PIPENV_DONT_USE_ASDF = bool(os.environ.get("PIPENV_DONT_USE_ASDF")) +"""If set, Pipenv does not attempt to install Python with asdf. + +Default is to install Python automatically via asdf when needed, if possible. +""" + PIPENV_DOTENV_LOCATION = os.environ.get("PIPENV_DOTENV_LOCATION") """If set, Pipenv loads the ``.env`` file at the specified location. @@ -151,6 +157,7 @@ if PIPENV_IS_CI: PIPENV_NOSPIN = True PIPENV_SPINNER = "dots" if not os.name == "nt" else "bouncingBar" +PIPENV_SPINNER = os.environ.get("PIPENV_SPINNER", PIPENV_SPINNER) """Sets the default spinner type. Spinners are identitcal to the node.js spinners and can be found at diff --git a/pipenv/exceptions.py b/pipenv/exceptions.py index c8ca0dc8..6e0032ad 100644 --- a/pipenv/exceptions.py +++ b/pipenv/exceptions.py @@ -84,7 +84,7 @@ class PipenvException(ClickException): file = vistir.misc.get_text_stderr() if self.extra: if isinstance(self.extra, STRING_TYPES): - self.extra = [self.extra,] + self.extra = [self.extra] for extra in self.extra: extra = "[pipenv.exceptions.{0!s}]: {1}".format( self.__class__.__name__, extra @@ -163,14 +163,13 @@ class PipenvUsageError(UsageError): color = self.ctx.color if self.extra: if isinstance(self.extra, STRING_TYPES): - self.extra = [self.extra,] + self.extra = [self.extra] for extra in self.extra: if color: extra = getattr(crayons, color, "blue")(extra) click_echo(decode_for_output(extra, file), file=file) hint = '' - if (self.cmd is not None and - self.cmd.get_help_option(self.ctx) is not None): + if self.cmd is not None and self.cmd.get_help_option(self.ctx) is not None: hint = ('Try "%s %s" for help.\n' % (self.ctx.command_path, self.ctx.help_option_names[0])) if self.ctx is not None: @@ -199,7 +198,7 @@ class PipenvFileError(FileError): file = vistir.misc.get_text_stderr() if self.extra: if isinstance(self.extra, STRING_TYPES): - self.extra = [self.extra,] + self.extra = [self.extra] for extra in self.extra: click_echo(decode_for_output(extra, file), file=file) click_echo(self.message, file=file) @@ -312,7 +311,7 @@ class VirtualenvCreationException(VirtualenvException): extra = ANSI_REMOVAL_RE.sub("", "{0}".format(extra)) if "KeyboardInterrupt" in extra: extra = crayons.red("Virtualenv creation interrupted by user", bold=True) - self.extra = extra = [extra,] + self.extra = extra = [extra] VirtualenvException.__init__(self, message, extra=extra) @@ -321,9 +320,9 @@ class UninstallError(PipenvException): extra = [ "{0} {1}".format( crayons.blue("Attempted to run command: "), - crayons.yellow("$ {0!r}".format(command), bold=True + crayons.yellow("$ {0!r}".format(command), bold=True) ) - )] + ] extra.extend([crayons.blue(line.strip()) for line in return_values.splitlines()]) if isinstance(package, (tuple, list, set)): package = " ".join(package) diff --git a/pipenv/pyenv.py b/pipenv/installers.py similarity index 61% rename from pipenv/pyenv.py rename to pipenv/installers.py index 941e5991..cd8e49a2 100644 --- a/pipenv/pyenv.py +++ b/pipenv/installers.py @@ -48,19 +48,22 @@ class Version(object): return (self.major, self.minor) == (other.major, other.minor) -class PyenvError(RuntimeError): +class InstallerError(RuntimeError): def __init__(self, desc, c): - super(PyenvError, self).__init__(desc) + super(InstallerError, self).__init__(desc) self.out = c.out self.err = c.err -class Runner(object): +class Installer(object): - def __init__(self, pyenv): - self._cmd = pyenv + def __init__(self, cmd): + self._cmd = cmd - def _pyenv(self, *args, **kwargs): + def __str__(self): + return self._cmd + + def _run(self, *args, **kwargs): timeout = kwargs.pop('timeout', delegator.TIMEOUT) if kwargs: k = list(kwargs.keys())[0] @@ -69,21 +72,16 @@ class Runner(object): c = delegator.run(args, block=False, timeout=timeout) c.block() if c.return_code != 0: - raise PyenvError('faild to run {0}'.format(args), c) + raise InstallerError('faild to run {0}'.format(args), c) return c def iter_installable_versions(self): """Iterate through CPython versions available for Pipenv to install. """ - for name in self._pyenv('install', '--list').out.splitlines(): - try: - version = Version.parse(name.strip()) - except ValueError: - continue - yield version + raise NotImplementedError def find_version_to_install(self, name): - """Find a version in pyenv from the version supplied. + """Find a version in the installer from the version supplied. A ValueError is raised if a matching version cannot be found. """ @@ -103,16 +101,64 @@ class Runner(object): return best_match def install(self, version): - """Install the given version with pyenv. + """Install the given version with runner implementation. The version must be a ``Version`` instance representing a version - found in pyenv. + found in the Installer. A ValueError is raised if the given version does not have a match in - pyenv. A PyenvError is raised if the pyenv command fails. + the runner. A InstallerError is raised if the runner command fails. """ - c = self._pyenv( + raise NotImplementedError + + +class Pyenv(Installer): + + def iter_installable_versions(self): + """Iterate through CPython versions available for Pipenv to install. + """ + for name in self._run('install', '--list').out.splitlines(): + try: + version = Version.parse(name.strip()) + except ValueError: + continue + yield version + + def install(self, version): + """Install the given version with pyenv. + The version must be a ``Version`` instance representing a version + found in pyenv. + A ValueError is raised if the given version does not have a match in + pyenv. A InstallerError is raised if the pyenv command fails. + """ + c = self._run( 'install', '-s', str(version), timeout=PIPENV_INSTALL_TIMEOUT, ) return c + + +class Asdf(Installer): + + def iter_installable_versions(self): + """Iterate through CPython versions available for asdf to install. + """ + for name in self._run('list-all', 'python').out.splitlines(): + try: + version = Version.parse(name.strip()) + except ValueError: + continue + yield version + + def install(self, version): + """Install the given version with asdf. + The version must be a ``Version`` instance representing a version + found in asdf. + A ValueError is raised if the given version does not have a match in + asdf. A InstallerError is raised if the asdf command fails. + """ + c = self._run( + 'install', 'python', str(version), + timeout=PIPENV_INSTALL_TIMEOUT, + ) + return c diff --git a/pipenv/pipenv.1 b/pipenv/pipenv.1 index 6cd64a4c..7dd63f58 100644 --- a/pipenv/pipenv.1 +++ b/pipenv/pipenv.1 @@ -275,7 +275,7 @@ If you don\(aqt even have pip installed, you can use this crude installation met .sp .nf .ft C -$ curl https://raw.githubusercontent.com/kennethreitz/pipenv/master/get\-pipenv.py | python +$ curl https://raw.githubusercontent.com/pypa/pipenv/master/get\-pipenv.py | python .ft P .fi .UNINDENT @@ -4121,6 +4121,6 @@ search .SH AUTHOR Kenneth Reitz .SH COPYRIGHT -2017. A Kenneth Reitz Project +2017. A project founded by Kenneth Reitz .\" Generated by docutils manpage writer. . diff --git a/pipenv/project.py b/pipenv/project.py index c4b0c941..5b14106e 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -15,8 +15,6 @@ import toml import tomlkit import vistir -from first import first - import pipfile import pipfile.api @@ -209,14 +207,12 @@ class Project(object): # First exclude anything that is a vcs entry either in the key or value if not ( any(is_vcs(i) for i in [k, v]) - or # Then exclude any installable files that are not directories # Because pip-tools can resolve setup.py for example - any(is_installable_file(i) for i in [k, v]) - or + or any(is_installable_file(i) for i in [k, v]) # Then exclude any URLs because they need to be editable also # Things that are excluded can only be 'shallow resolved' - any(is_valid_url(i) for i in [k, v]) + or any(is_valid_url(i) for i in [k, v]) ): ps.update({k: v}) return ps @@ -337,7 +333,7 @@ class Project(object): if not self._environment: prefix = self.virtualenv_location is_venv = is_in_virtualenv() - sources = self.sources if self.sources else [DEFAULT_SOURCE,] + sources = self.sources if self.sources else [DEFAULT_SOURCE] self._environment = Environment( prefix=prefix, is_venv=is_venv, sources=sources, pipfile=self.parsed_pipfile, project=self @@ -421,8 +417,10 @@ class Project(object): def virtualenv_location(self): # if VIRTUAL_ENV is set, use that. virtualenv_env = os.getenv("VIRTUAL_ENV") - if ("PIPENV_ACTIVE" not in os.environ and - not PIPENV_IGNORE_VIRTUALENVS and virtualenv_env): + if ( + "PIPENV_ACTIVE" not in os.environ + and not PIPENV_IGNORE_VIRTUALENVS and virtualenv_env + ): return virtualenv_env if not self._virtualenv_location: # Use cached version, if available. @@ -542,7 +540,6 @@ class Project(object): def build_requires(self): return self._build_system.get("requires", ["setuptools>=40.8.0", "wheel"]) - @property def build_backend(self): return self._build_system.get("build-backend", get_default_pyproject_backend()) @@ -688,7 +685,7 @@ class Project(object): .lstrip("\n") .split("\n") ) - sources = [DEFAULT_SOURCE,] + sources = [DEFAULT_SOURCE] for i, index in enumerate(indexes): if not index: continue @@ -756,7 +753,7 @@ class Project(object): if not sources: sources = self.pipfile_sources elif not isinstance(sources, list): - sources = [sources,] + sources = [sources] lockfile_dict["_meta"]["sources"] = [ self.populate_source(s) for s in sources ] @@ -775,7 +772,7 @@ class Project(object): else: sources = [dict(source) for source in self.parsed_pipfile["source"]] if not isinstance(sources, list): - sources = [sources,] + sources = [sources] return { "hash": {"sha256": self.calculate_pipfile_hash()}, "pipfile-spec": PIPFILE_SPEC_CURRENT, diff --git a/pipenv/resolver.py b/pipenv/resolver.py index 1219cc24..cd04fccb 100644 --- a/pipenv/resolver.py +++ b/pipenv/resolver.py @@ -516,7 +516,6 @@ class Entry(object): :raises: :exc:`~pipenv.exceptions.DependencyConflict` if resolution is impossible """ # ensure that we satisfy the parent dependencies of this dep - from pipenv.vendor.packaging.specifiers import Specifier parent_dependencies = set() has_mismatch = False can_use_original = True @@ -527,7 +526,6 @@ class Entry(object): # parents with no requirements can't conflict if not p.requirements: continue - needed = p.requirements.get("dependencies", []) entry_ref = p.get_dependency(self.name) required = entry_ref.get("required_version", "*") required = self.clean_specifier(required) @@ -633,7 +631,6 @@ def clean_results(results, resolver, project, dev=False): return results lockfile = project.lockfile_content section = "develop" if dev else "default" - pipfile_section = "dev-packages" if dev else "packages" reverse_deps = project.environment.reverse_dependencies() new_results = [r for r in results if r["name"] not in lockfile[section]] for result in results: @@ -646,17 +643,11 @@ def clean_results(results, resolver, project, dev=False): def clean_outdated(results, resolver, project, dev=False): - from pipenv.vendor.requirementslib.models.requirements import Requirement - from pipenv.environments import is_verbose if not project.lockfile_exists: return results lockfile = project.lockfile_content section = "develop" if dev else "default" - pipfile_section = "dev-packages" if dev else "packages" - pipfile = project.parsed_pipfile[pipfile_section] reverse_deps = project.environment.reverse_dependencies() - deptree = project.environment.get_package_requirements() - overlapping_results = [r for r in results if r["name"] in lockfile[section]] new_results = [r for r in results if r["name"] not in lockfile[section]] for result in results: name = result.get("name") diff --git a/pipenv/utils.py b/pipenv/utils.py index 2b0a8cdb..32f4491c 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -1,46 +1,41 @@ # -*- coding: utf-8 -*- from __future__ import print_function + import contextlib import errno import logging import os import posixpath import re -import signal import shutil +import signal import stat import sys import warnings - from contextlib import contextmanager from distutils.spawn import find_executable import six import toml -import tomlkit - from click import echo as click_echo from six.moves.urllib.parse import urlparse -from .vendor.vistir.compat import ResourceWarning, lru_cache, Mapping, Sequence, Set -from .vendor.vistir.misc import fs_str, run import crayons import parse +import tomlkit from . import environments -from .exceptions import ( - PipenvUsageError, RequirementError, PipenvCmdError, ResolutionFailure -) +from .exceptions import PipenvCmdError, PipenvUsageError, RequirementError, ResolutionFailure from .pep508checker import lookup +from .vendor.packaging.markers import Marker from .vendor.urllib3 import util as urllib3_util - +from .vendor.vistir.compat import Mapping, ResourceWarning, Sequence, Set, lru_cache +from .vendor.vistir.misc import fs_str, run if environments.MYPY_RUNNING: from typing import Tuple, Dict, Any, List, Union, Optional, Text from .vendor.requirementslib.models.requirements import Requirement, Line from .vendor.requirementslib.models.pipfile import Pipfile - from .vendor.packaging.markers import Marker - from .vendor.packaging.specifiers import Specifier from .project import Project, TSource @@ -316,7 +311,7 @@ def get_source_list( sources.append(get_project_index(index)) if extra_indexes: if isinstance(extra_indexes, six.string_types): - extra_indexes = [extra_indexes,] + extra_indexes = [extra_indexes] for source in extra_indexes: extra_src = get_project_index(source) if not sources or extra_src["url"] != sources[0]["url"]: @@ -555,8 +550,8 @@ class Resolver(object): # but leave it on for local, installable folders on the filesystem if environments.PIPENV_RESOLVE_VCS or ( req.editable or parsed_line.is_wheel or ( - req.is_file_or_url and parsed_line.is_local and - is_installable_dir(parsed_line.path) + req.is_file_or_url and parsed_line.is_local + and is_installable_dir(parsed_line.path) ) ): requirements = [v for v in getattr(setup_info, "requires", {}).values()] @@ -920,8 +915,10 @@ class Resolver(object): # We also don't want to try to hash directories as this will fail # as these are editable deps and are not hashable. - if (ireq.link.scheme == "file" and - Path(to_native_string(url_to_path(ireq.link.url))).is_dir()): + if ( + ireq.link.scheme == "file" + and Path(to_native_string(url_to_path(ireq.link.url))).is_dir() + ): return False return True @@ -1069,7 +1066,6 @@ def actually_resolve_deps( req_dir=None, ): from pipenv.vendor.vistir.path import create_tracked_tempdir - from pipenv.vendor.requirementslib.models.requirements import Requirement if not req_dir: req_dir = create_tracked_tempdir(suffix="-requirements", prefix="pipenv-") @@ -1290,7 +1286,11 @@ def venv_resolve_deps( os.environ["PIPENV_VERBOSITY"] = str(environments.PIPENV_VERBOSITY) os.environ["PIPENV_REQ_DIR"] = fs_str(req_dir) os.environ["PIP_NO_INPUT"] = fs_str("1") - os.environ["PIPENV_SITE_DIR"] = get_pipenv_sitedir() + pipenv_site_dir = get_pipenv_sitedir() + if pipenv_site_dir is not None: + os.environ["PIPENV_SITE_DIR"] = pipenv_site_dir + else: + os.environ.pop("PIPENV_SITE_DIR", None) if keep_outdated: os.environ["PIPENV_KEEP_OUTDATED"] = fs_str("1") with create_spinner(text=decode_for_output("Locking...")) as sp: @@ -1889,7 +1889,7 @@ def translate_markers(pipfile_entry): """ if not isinstance(pipfile_entry, Mapping): raise TypeError("Entry is not a pipfile formatted mapping.") - from .vendor.packaging.markers import Marker, default_environment + from .vendor.packaging.markers import default_environment from .vendor.vistir.misc import dedup allowed_marker_keys = ["markers"] + list(default_environment().keys()) @@ -2217,8 +2217,8 @@ def is_python_command(line): from pipenv.vendor.pythonfinder.utils import PYTHON_IMPLEMENTATIONS is_version = re.match(r'[\d\.]+', line) - if (line.startswith("python") or is_version or - any(line.startswith(v) for v in PYTHON_IMPLEMENTATIONS)): + if (line.startswith("python") or is_version + or any(line.startswith(v) for v in PYTHON_IMPLEMENTATIONS)): return True # we are less sure about this but we can guess if line.startswith("py"): diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index da966ec9..00000000 --- a/pytest.ini +++ /dev/null @@ -1,25 +0,0 @@ -[pytest] -addopts = -ra -n auto -plugins = xdist -testpaths = tests -; Add vendor and patched in addition to the default list of ignored dirs -; Additionally, ignore tasks, news, test subdirectories and peeps directory -norecursedirs = - .* build - dist - CVS - _darcs - {arch} - *.egg - vendor - patched - news - tasks - docs - tests/test_artifacts - tests/pytest-pypi - tests/pypi - peeps -filterwarnings = - ignore::DeprecationWarning - ignore::PendingDeprecationWarning diff --git a/setup.cfg b/setup.cfg index 896a5a91..fbc691d4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,9 @@ license = MIT license_file = LICENSE [flake8] -exclude = .git,__pycache__,docs/,pipenv/vendor/,get-pipenv.py,setup.py +exclude = + .git,__pycache__,docs/,pipenv/vendor/,pipenv/patched,get-pipenv.py, + .eggs/,setup.py,tests/fixtures/ ignore = # The default ignore list: E121,E123,E126,E226,E24,E704, @@ -17,13 +19,14 @@ ignore = # E402: module level import not at top of file # E501: line too long # W503: line break before binary operator - E402,E501,W503 + E402,E501,W503,E203 [isort] atomic=true lines_after_imports=2 lines_between_types=1 multi_line_output=5 +line_length=80 not_skip=__init__.py known_first_party = pipenv @@ -36,3 +39,29 @@ follow_imports=skip html_report=mypyhtml python_version=3.6 mypy_path=typeshed/pyi:typeshed/imports + +[tool:pytest] +addopts = -ra -n auto +plugins = xdist +testpaths = tests +; Add vendor and patched in addition to the default list of ignored dirs +; Additionally, ignore tasks, news, test subdirectories and peeps directory +norecursedirs = + .* build + dist + CVS + _darcs + {arch} + *.egg + vendor + patched + news + tasks + docs + tests/test_artifacts + tests/pytest-pypi + tests/pypi + peeps +filterwarnings = + ignore::DeprecationWarning + ignore::PendingDeprecationWarning diff --git a/setup.py b/setup.py index d86d85e0..c3ee913b 100644 --- a/setup.py +++ b/setup.py @@ -45,6 +45,7 @@ extras = { "tests": ["pytest<5.0", "pytest-tap", "pytest-xdist", "flaky", "mock"], } + # https://pypi.python.org/pypi/stdeb/0.8.5#quickstart-2-just-tell-me-the-fastest-way-to-make-a-deb class DebCommand(Command): """Support for setup.py deb""" @@ -117,8 +118,8 @@ setup( description="Python Development Workflow for Humans.", long_description=long_description, long_description_content_type='text/markdown', - author="Kenneth Reitz", - author_email="me@kennethreitz.org", + author="Pipenv maintainer team", + author_email="distutils-sig@python.org", url="https://github.com/pypa/pipenv", packages=find_packages(exclude=["tests", "tests.*", "tasks", "tasks.*"]), entry_points={ diff --git a/tasks/__init__.py b/tasks/__init__.py index 63fe1388..d81d101d 100644 --- a/tasks/__init__.py +++ b/tasks/__init__.py @@ -1,8 +1,6 @@ # -*- coding=utf-8 -*- # Copied from pip's vendoring process # see https://github.com/pypa/pip/blob/95bcf8c5f6394298035a7332c441868f3b0169f4/tasks/__init__.py -import re - from pathlib import Path import invoke diff --git a/tasks/release.py b/tasks/release.py index 610a36a9..c43a7c85 100644 --- a/tasks/release.py +++ b/tasks/release.py @@ -3,7 +3,6 @@ import datetime import os import pathlib import re -import sys import invoke @@ -240,8 +239,6 @@ def bump_version(ctx, dry_run=False, dev=False, pre=False, tag=None, commit=Fals current_version = Version.parse(__version__) today = datetime.date.today() tomorrow = today + datetime.timedelta(days=1) - next_month = datetime.date.today().replace(month=today.month + 1, day=1) - next_year = datetime.date.today().replace(year=today.year + 1, month=1, day=1) if pre and not tag: print('Using "pre" requires a corresponding tag.') return diff --git a/tasks/vendoring/__init__.py b/tasks/vendoring/__init__.py index 69398450..8329ba4e 100644 --- a/tasks/vendoring/__init__.py +++ b/tasks/vendoring/__init__.py @@ -41,7 +41,7 @@ LIBRARY_DIRNAMES = { 'enum': 'backports/enum' } -PY2_DOWNLOAD = ['enum34',] +PY2_DOWNLOAD = ['enum34'] # from time to time, remove the no longer needed ones HARDCODED_LICENSE_URLS = { @@ -93,7 +93,6 @@ LICENSE_RENAMES = { } - def drop_dir(path): if path.exists() and path.is_dir(): shutil.rmtree(str(path), ignore_errors=True) @@ -379,7 +378,6 @@ def vendor(ctx, vendor_dir, package=None, rewrite=True): post_install_cleanup(ctx, vendor_dir) # Detect the vendored packages/modules vendored_libs = detect_vendored_libs(_get_vendor_dir(ctx)) - patched_libs = detect_vendored_libs(_get_patched_dir(ctx)) log("Detected vendored libraries: %s" % ", ".join(vendored_libs)) # Apply pre-patches @@ -509,14 +507,6 @@ def download_licenses( new_requirements_file = fh.name new_requirements_file = Path(new_requirements_file) log(requirements) - requirement = "-r {0}".format(new_requirements_file.as_posix()) - if package: - if not only: - # for packages we want to add to the requirements file - requirement = _ensure_package_in_requirements(ctx, requirements_file, package) - else: - # for packages we want to get the license for by themselves - requirement = package tmp_dir = vendor_dir / '__tmp__' # TODO: Fix this whenever it gets sorted out (see https://github.com/pypa/pip/issues/5739) cmd = "pip download --no-binary :all: --only-binary requests_download --no-deps" diff --git a/tasks/vendoring/vendor_passa.py b/tasks/vendoring/vendor_passa.py index f2c58745..2da91259 100644 --- a/tasks/vendoring/vendor_passa.py +++ b/tasks/vendoring/vendor_passa.py @@ -2,7 +2,7 @@ import invoke from pipenv._compat import TemporaryDirectory -from . import _get_git_root, _get_vendor_dir, log +from . import _get_vendor_dir, log @invoke.task diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index b05eacfa..53973d34 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,30 +1,34 @@ # -*- coding=utf-8 -*- from __future__ import absolute_import, print_function + import errno import json import logging import os import shutil -import signal -import socket import sys -import time import warnings -from shutil import copyfileobj, rmtree as _rmtree +from shutil import rmtree as _rmtree import pytest import requests -from pipenv.vendor.vistir.compat import ResourceWarning, fs_str, fs_encode, FileNotFoundError, PermissionError, TemporaryDirectory -from pipenv.vendor.vistir.misc import run -from pipenv.vendor.vistir.contextmanagers import temp_environ -from pipenv.vendor.vistir.path import mkdir_p, create_tracked_tempdir, handle_remove_readonly - from pipenv._compat import Path from pipenv.exceptions import VirtualenvActivationException from pipenv.vendor import delegator, toml, tomlkit -from pytest_pypi.app import prepare_fixtures, prepare_packages as prepare_pypi_packages +from pipenv.vendor.vistir.compat import ( + FileNotFoundError, PermissionError, ResourceWarning, TemporaryDirectory, + fs_encode, fs_str +) +from pipenv.vendor.vistir.contextmanagers import temp_environ +from pipenv.vendor.vistir.misc import run +from pipenv.vendor.vistir.path import ( + create_tracked_tempdir, handle_remove_readonly, mkdir_p +) +from pytest_pypi.app import prepare_fixtures +from pytest_pypi.app import prepare_packages as prepare_pypi_packages + log = logging.getLogger(__name__) warnings.simplefilter("default", category=ResourceWarning) @@ -399,7 +403,6 @@ class _PipenvInstance(object): def _rmtree_func(path, ignore_errors=True, onerror=None): directory = fs_encode(path) - global _rmtree shutil_rmtree = _rmtree if onerror is None: onerror = handle_remove_readonly diff --git a/tests/integration/test_cli.py b/tests/integration/test_cli.py index 1ee9f64a..04253fc5 100644 --- a/tests/integration/test_cli.py +++ b/tests/integration/test_cli.py @@ -223,7 +223,7 @@ def test_install_parse_error(PipenvInstance): [dev-packages] """.strip() f.write(contents) - c = p.pipenv('install requests u/\\/p@r\$34b13+pkg') + c = p.pipenv('install requests u/\\/p@r\\$34b13+pkg') assert c.return_code != 0 assert 'u/\\/p@r$34b13+pkg' not in p.pipfile['packages'] @@ -265,3 +265,16 @@ def test_pipenv_three(PipenvInstance): c = p.pipenv('--three') assert c.return_code == 0 assert 'Successfully created virtual environment' in c.err + + +@pytest.mark.outdated +def test_pipenv_outdated_prerelease(PipenvInstance): + with PipenvInstance(chdir=True) as p: + with open(p.pipfile_path, "w") as f: + contents = """ +[packages] +sqlalchemy = "<=1.2.3" + """.strip() + f.write(contents) + c = p.pipenv('update --pre --outdated') + assert c.return_code == 0 diff --git a/tests/integration/test_dot_venv.py b/tests/integration/test_dot_venv.py index aa52dd5e..3cbc88db 100644 --- a/tests/integration/test_dot_venv.py +++ b/tests/integration/test_dot_venv.py @@ -5,9 +5,7 @@ import os import pytest from pipenv._compat import Path, TemporaryDirectory -from pipenv.project import Project -from pipenv.utils import get_windows_path, normalize_drive, temp_environ -from pipenv.vendor import delegator +from pipenv.utils import normalize_drive, temp_environ @pytest.mark.dotvenv diff --git a/tests/integration/test_install_basic.py b/tests/integration/test_install_basic.py index 80ccdf0e..a6beb21d 100644 --- a/tests/integration/test_install_basic.py +++ b/tests/integration/test_install_basic.py @@ -7,7 +7,6 @@ import pytest from flaky import flaky from pipenv._compat import Path, TemporaryDirectory -from pipenv.project import Project from pipenv.utils import temp_environ from pipenv.vendor import delegator diff --git a/tests/integration/test_install_markers.py b/tests/integration/test_install_markers.py index 00f9c789..de3ba193 100644 --- a/tests/integration/test_install_markers.py +++ b/tests/integration/test_install_markers.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function import os -import sys import pytest diff --git a/tests/integration/test_install_twists.py b/tests/integration/test_install_twists.py index 44973df5..4fcbd4c5 100644 --- a/tests/integration/test_install_twists.py +++ b/tests/integration/test_install_twists.py @@ -9,7 +9,6 @@ import pytest from flaky import flaky from pipenv._compat import Path -from pipenv.project import Project from pipenv.utils import mkdir_p, temp_environ from pipenv.vendor import delegator @@ -40,7 +39,6 @@ setup( """.strip() fh.write(contents) line = "-e .[dev]" - pipfile = {"testpipenv": {"path": ".", "editable": True, "extras": ["dev"]}} with open(os.path.join(p.path, 'Pipfile'), 'w') as fh: fh.write(""" [packages] diff --git a/tests/integration/test_lock.py b/tests/integration/test_lock.py index 4c227395..7d207cca 100644 --- a/tests/integration/test_lock.py +++ b/tests/integration/test_lock.py @@ -15,7 +15,7 @@ from pipenv.utils import temp_environ @pytest.mark.lock @pytest.mark.requirements def test_lock_handle_eggs(PipenvInstance): - """Ensure locking works with packages provoding egg formats. + """Ensure locking works with packages providing egg formats. """ with PipenvInstance() as p: with open(p.pipfile_path, 'w') as f: @@ -174,7 +174,7 @@ def test_complex_lock_with_vcs_deps(PipenvInstance, pip_src_dir): click = "==6.7" [dev-packages] -requests = {git = "https://github.com/kennethreitz/requests.git"} +requests = {git = "https://github.com/psf/requests.git"} """.strip() f.write(contents) @@ -429,7 +429,7 @@ def test_lock_editable_vcs_without_install(PipenvInstance): with open(p.pipfile_path, 'w') as f: f.write(""" [packages] -requests = {git = "https://github.com/kennethreitz/requests.git", ref = "master", editable = true} +requests = {git = "https://github.com/psf/requests.git", ref = "master", editable = true} """.strip()) c = p.pipenv('lock') assert c.return_code == 0 @@ -448,11 +448,11 @@ def test_lock_editable_vcs_with_ref_in_git(PipenvInstance): with open(p.pipfile_path, 'w') as f: f.write(""" [packages] -requests = {git = "https://github.com/kennethreitz/requests.git@883caaf", editable = true} +requests = {git = "https://github.com/psf/requests.git@883caaf", editable = true} """.strip()) c = p.pipenv('lock') assert c.return_code == 0 - assert p.lockfile['default']['requests']['git'] == 'https://github.com/kennethreitz/requests.git' + assert p.lockfile['default']['requests']['git'] == 'https://github.com/psf/requests.git' assert p.lockfile['default']['requests']['ref'] == '883caaf145fbe93bd0d208a6b864de9146087312' c = p.pipenv('install') assert c.return_code == 0 @@ -466,11 +466,11 @@ def test_lock_editable_vcs_with_ref(PipenvInstance): with open(p.pipfile_path, 'w') as f: f.write(""" [packages] -requests = {git = "https://github.com/kennethreitz/requests.git", ref = "883caaf", editable = true} +requests = {git = "https://github.com/psf/requests.git", ref = "883caaf", editable = true} """.strip()) c = p.pipenv('lock') assert c.return_code == 0 - assert p.lockfile['default']['requests']['git'] == 'https://github.com/kennethreitz/requests.git' + assert p.lockfile['default']['requests']['git'] == 'https://github.com/psf/requests.git' assert p.lockfile['default']['requests']['ref'] == '883caaf145fbe93bd0d208a6b864de9146087312' c = p.pipenv('install') assert c.return_code == 0 @@ -485,7 +485,7 @@ def test_lock_editable_vcs_with_extras_without_install(PipenvInstance): with open(p.pipfile_path, 'w') as f: f.write(""" [packages] -requests = {git = "https://github.com/kennethreitz/requests.git", editable = true, extras = ["socks"]} +requests = {git = "https://github.com/psf/requests.git", editable = true, extras = ["socks"]} """.strip()) c = p.pipenv('lock') assert c.return_code == 0 @@ -505,7 +505,7 @@ def test_lock_editable_vcs_with_markers_without_install(PipenvInstance): with open(p.pipfile_path, 'w') as f: f.write(""" [packages] -requests = {git = "https://github.com/kennethreitz/requests.git", ref = "master", editable = true, markers = "python_version >= '2.6'"} +requests = {git = "https://github.com/psf/requests.git", ref = "master", editable = true, markers = "python_version >= '2.6'"} """.strip()) c = p.pipenv('lock') assert c.return_code == 0 @@ -659,4 +659,4 @@ six = "*" c = p.pipenv("lock --clear") assert c.return_code == 0 assert "index" in p.lockfile["default"]["six"] - assert p.lockfile["default"]["six"]["index"] == "custom", Path(p.lockfile_path).read_text() # p.lockfile["default"]["six"] + assert p.lockfile["default"]["six"]["index"] == "custom", Path(p.lockfile_path).read_text() # p.lockfile["default"]["six"] diff --git a/tests/integration/test_project.py b/tests/integration/test_project.py index f7b0e460..bef9912f 100644 --- a/tests/integration/test_project.py +++ b/tests/integration/test_project.py @@ -11,7 +11,6 @@ from pipenv.project import Project from pipenv.utils import temp_environ from pipenv.vendor.vistir.path import is_in_path from pipenv.vendor.delegator import run as delegator_run -import pipenv.environments @pytest.mark.project diff --git a/tests/integration/test_windows.py b/tests/integration/test_windows.py index b303d0ab..a74be386 100644 --- a/tests/integration/test_windows.py +++ b/tests/integration/test_windows.py @@ -5,7 +5,6 @@ import os import pytest from pipenv._compat import Path -from pipenv.project import Project # This module is run only on Windows. diff --git a/tests/pytest-pypi/pytest_pypi/app.py b/tests/pytest-pypi/pytest_pypi/app.py index 95dbd076..5484eeb7 100644 --- a/tests/pytest-pypi/pytest_pypi/app.py +++ b/tests/pytest-pypi/pytest_pypi/app.py @@ -4,7 +4,6 @@ import contextlib import io import json import os -import sys from tarfile import is_tarfile from zipfile import is_zipfile diff --git a/tests/pytest-pypi/pytest_pypi/certs.py b/tests/pytest-pypi/pytest_pypi/certs.py index f9e33870..b73fc63a 100644 --- a/tests/pytest-pypi/pytest_pypi/certs.py +++ b/tests/pytest-pypi/pytest_pypi/certs.py @@ -17,5 +17,6 @@ def where(): # vendored bundle inside Requests return os.path.join(os.path.abspath(os.path.dirname(__file__)), 'certs', 'cacert.pem') + if __name__ == '__main__': print(where()) diff --git a/tests/pytest-pypi/pytest_pypi/plugin.py b/tests/pytest-pypi/pytest_pypi/plugin.py index a17fcb24..83cd73fb 100644 --- a/tests/pytest-pypi/pytest_pypi/plugin.py +++ b/tests/pytest-pypi/pytest_pypi/plugin.py @@ -3,6 +3,7 @@ import pytest from .app import app as pypi_app from . import serve, certs + @pytest.fixture(scope='session') def pypi(request): server = serve.Server(application=pypi_app) @@ -31,6 +32,7 @@ def pypi_both(request, pypi, pypi_secure): def class_based_pypi(request, pypi): request.cls.pypi = pypi + @pytest.fixture(scope='class') def class_based_pypi_secure(request, pypi_secure): request.cls.pypi_secure = pypi_secure diff --git a/tests/unit/test_patched.py b/tests/unit/test_patched.py index 249292b4..03e1c039 100644 --- a/tests/unit/test_patched.py +++ b/tests/unit/test_patched.py @@ -122,6 +122,7 @@ get_extras_links_scenarios = { ), } + @pytest.mark.parametrize( 'scenarios,expected', list(get_extras_links_scenarios.values()), diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 18cb94a8..d3363037 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -3,11 +3,7 @@ import os import pytest -from first import first -from mock import Mock, patch - import pipenv.utils -import pythonfinder.utils from pipenv.exceptions import PipenvUsageError @@ -129,11 +125,8 @@ def test_convert_deps_to_pip(monkeypatch, deps, expected): ), ], ) -def test_convert_deps_to_pip_one_way(monkeypatch, deps, expected): - with monkeypatch.context() as m: - import pip_shims - # m.setattr(pip_shims.shims, "unpack_url", mock_unpack) - assert pipenv.utils.convert_deps_to_pip(deps, r=False) == [expected.lower()] +def test_convert_deps_to_pip_one_way(deps, expected): + assert pipenv.utils.convert_deps_to_pip(deps, r=False) == [expected.lower()] @pytest.mark.skipif(isinstance(u"", str), reason="don't need to test if unicode is str") @@ -218,20 +211,20 @@ class TestUtils: @pytest.mark.windows @pytest.mark.skipif(os.name != "nt", reason="Windows test only") def test_windows_shellquote(self): - test_path = "C:\Program Files\Python36\python.exe" + test_path = r"C:\Program Files\Python36\python.exe" expected_path = '"C:\\\\Program Files\\\\Python36\\\\python.exe"' assert pipenv.utils.escape_grouped_arguments(test_path) == expected_path @pytest.mark.utils def test_is_valid_url(self): - url = "https://github.com/kennethreitz/requests.git" + url = "https://github.com/psf/requests.git" not_url = "something_else" assert pipenv.utils.is_valid_url(url) assert pipenv.utils.is_valid_url(not_url) is False @pytest.mark.utils def test_download_file(self): - url = "https://github.com/kennethreitz/pipenv/blob/master/README.md" + url = "https://github.com/pypa/pipenv/blob/master/README.md" output = "test_download.md" pipenv.utils.download_file(url, output) assert os.path.exists(output)