diff --git a/pipenv/vendor/dotenv/cli.py b/pipenv/vendor/dotenv/cli.py index dd7c2418..4e03c12a 100644 --- a/pipenv/vendor/dotenv/cli.py +++ b/pipenv/vendor/dotenv/cli.py @@ -8,7 +8,8 @@ except ImportError: 'Run pip install "python-dotenv[cli]" to fix this.') sys.exit(1) -from .main import dotenv_values, get_key, set_key, unset_key +from .main import dotenv_values, get_key, set_key, unset_key, run_command +from .version import __version__ @click.group() @@ -18,6 +19,7 @@ from .main import dotenv_values, get_key, set_key, unset_key @click.option('-q', '--quote', default='always', type=click.Choice(['always', 'never', 'auto']), help="Whether to quote or not the variable values. Default mode is always. This does not affect parsing.") +@click.version_option(version=__version__) @click.pass_context def cli(ctx, file, quote): '''This script is used to set, get or unset values from a .env file.''' @@ -78,5 +80,19 @@ def unset(ctx, key): exit(1) +@cli.command(context_settings={'ignore_unknown_options': True}) +@click.pass_context +@click.argument('commandline', nargs=-1, type=click.UNPROCESSED) +def run(ctx, commandline): + """Run command with environment variables present.""" + file = ctx.obj['FILE'] + dotenv_as_dict = dotenv_values(file) + if not commandline: + click.echo('No command given.') + exit(1) + ret = run_command(commandline, dotenv_as_dict) + exit(ret) + + if __name__ == "__main__": cli() diff --git a/pipenv/vendor/dotenv/main.py b/pipenv/vendor/dotenv/main.py index 75f49c4a..6ba28bbb 100644 --- a/pipenv/vendor/dotenv/main.py +++ b/pipenv/vendor/dotenv/main.py @@ -7,6 +7,7 @@ import io import os import re import sys +from subprocess import Popen, PIPE, STDOUT import warnings from collections import OrderedDict @@ -30,7 +31,7 @@ def parse_line(line): k, v = line.split('=', 1) if k.startswith('export '): - k = k.lstrip('export ') + (_, _, k) = k.partition('export ') # Remove any leading and trailing spaces in key, value k, v = k.strip(), v.strip() @@ -238,7 +239,11 @@ def find_dotenv(filename='.env', raise_error_if_not_found=False, usecwd=False): path = os.getcwd() else: # will work for .py files - frame_filename = sys._getframe().f_back.f_code.co_filename + frame = sys._getframe() + # find first frame that is outside of this file + while frame.f_code.co_filename == __file__: + frame = frame.f_back + frame_filename = frame.f_code.co_filename path = os.path.dirname(os.path.abspath(frame_filename)) for dirname in _walk_to_root(path): @@ -260,3 +265,46 @@ def load_dotenv(dotenv_path=None, stream=None, verbose=False, override=False): def dotenv_values(dotenv_path=None, stream=None, verbose=False): f = dotenv_path or stream or find_dotenv() return DotEnv(f, verbose=verbose).dict() + + +def run_command(command, env): + """Run command in sub process. + + Runs the command in a sub process with the variables from `env` + added in the current environment variables. + + Parameters + ---------- + command: List[str] + The command and it's parameters + env: Dict + The additional environment variables + + Returns + ------- + int + The return code of the command + + """ + # copy the current environment variables and add the vales from + # `env` + cmd_env = os.environ.copy() + cmd_env.update(env) + + p = Popen(command, + stdin=PIPE, + stdout=PIPE, + stderr=STDOUT, + universal_newlines=True, + bufsize=0, + shell=False, + env=cmd_env) + try: + out, _ = p.communicate() + print(out) + except Exception: + warnings.warn('An error occured, running the command:') + out, _ = p.communicate() + warnings.warn(out) + + return p.returncode diff --git a/pipenv/vendor/dotenv/version.py b/pipenv/vendor/dotenv/version.py new file mode 100644 index 00000000..d69d16e9 --- /dev/null +++ b/pipenv/vendor/dotenv/version.py @@ -0,0 +1 @@ +__version__ = "0.9.1" diff --git a/pipenv/vendor/scandir.py b/pipenv/vendor/scandir.py index 2b2ade30..8bbae2c5 100644 --- a/pipenv/vendor/scandir.py +++ b/pipenv/vendor/scandir.py @@ -23,7 +23,6 @@ from os import listdir, lstat, stat, strerror from os.path import join, islink from stat import S_IFDIR, S_IFLNK, S_IFREG import collections -import os import sys _scandir = None @@ -38,7 +37,7 @@ if _scandir is None and ctypes is None: warnings.warn("scandir can't find the compiled _scandir C module " "or ctypes, using slow generic fallback") -__version__ = '1.7' +__version__ = '1.9.0' __all__ = ['scandir', 'walk'] # Windows FILE_ATTRIBUTE constants for interpreting the @@ -93,6 +92,10 @@ class GenericDirEntry(object): self._lstat = lstat(self.path) return self._lstat + # The code duplication below is intentional: this is for slightly + # better performance on systems that fall back to GenericDirEntry. + # It avoids an additional attribute lookup and method call, which + # are relatively slow on CPython. def is_dir(self, follow_symlinks=True): try: st = self.stat(follow_symlinks=follow_symlinks) @@ -416,6 +419,16 @@ elif sys.platform.startswith(('linux', 'darwin', 'sunos5')) or 'bsd' in sys.plat ('d_type', ctypes.c_byte), ('d_name', ctypes.c_char * 256), ) + elif 'openbsd' in sys.platform: + _fields_ = ( + ('d_ino', ctypes.c_uint64), + ('d_off', ctypes.c_uint64), + ('d_reclen', ctypes.c_uint16), + ('d_type', ctypes.c_uint8), + ('d_namlen', ctypes.c_uint8), + ('__d_padding', ctypes.c_uint8 * 4), + ('d_name', ctypes.c_char * 256), + ) else: _fields_ = ( ('d_ino', ctypes.c_uint32), # must be uint32, not ulong diff --git a/pipenv/vendor/semver.py b/pipenv/vendor/semver.py index dfeb431d..5f5be2c2 100644 --- a/pipenv/vendor/semver.py +++ b/pipenv/vendor/semver.py @@ -6,7 +6,7 @@ import collections import re -__version__ = '2.8.0' +__version__ = '2.8.1' __author__ = 'Kostiantyn Rybnikov' __author_email__ = 'k-bx@k-bx.com' __maintainer__ = 'Sebastien Celles' @@ -46,6 +46,19 @@ def parse(version): and 'prerelease'. The prerelease or build keys can be None if not provided :rtype: dict + + >>> import semver + >>> ver = semver.parse('3.4.5-pre.2+build.4') + >>> ver['major'] + 3 + >>> ver['minor'] + 4 + >>> ver['patch'] + 5 + >>> ver['prerelease'] + 'pre.2' + >>> ver['build'] + 'build.4' """ match = _REGEX.match(version) if match is None: @@ -60,8 +73,7 @@ def parse(version): return version_parts -class VersionInfo(collections.namedtuple( - 'VersionInfo', 'major minor patch prerelease build')): +class VersionInfo(object): """ :param int major: version when you make incompatible API changes. :param int minor: version when you add functionality in @@ -69,14 +81,48 @@ class VersionInfo(collections.namedtuple( :param int patch: version when you make backwards-compatible bug fixes. :param str prerelease: an optional prerelease string :param str build: an optional build string - - >>> import semver - >>> ver = semver.parse('3.4.5-pre.2+build.4') - >>> ver - {'build': 'build.4', 'major': 3, 'minor': 4, 'patch': 5, - 'prerelease': 'pre.2'} """ - __slots__ = () + __slots__ = ('_major', '_minor', '_patch', '_prerelease', '_build') + + def __init__(self, major, minor, patch, prerelease=None, build=None): + self._major = major + self._minor = minor + self._patch = patch + self._prerelease = prerelease + self._build = build + + @property + def major(self): + return self._major + + @property + def minor(self): + return self._minor + + @property + def patch(self): + return self._patch + + @property + def prerelease(self): + return self._prerelease + + @property + def build(self): + return self._build + + def _astuple(self): + return (self.major, self.minor, self.patch, + self.prerelease, self.build) + + def _asdict(self): + return collections.OrderedDict(( + ("major", self.major), + ("minor", self.minor), + ("patch", self.patch), + ("prerelease", self.prerelease), + ("build", self.build) + )) def __eq__(self, other): if not isinstance(other, (VersionInfo, dict)): @@ -108,11 +154,31 @@ class VersionInfo(collections.namedtuple( return NotImplemented return _compare_by_keys(self._asdict(), _to_dict(other)) >= 0 + def __repr__(self): + s = ", ".join("%s=%r" % (key, val) + for key, val in self._asdict().items()) + return "VersionInfo(%s)" % s + def __str__(self): - return format_version(*self) + return format_version(*(self._astuple())) def __hash__(self): - return hash(tuple(self)) + return hash(self._astuple()) + + @staticmethod + def parse(version): + """Parse version string to a VersionInfo instance. + + >>> from semver import VersionInfo + >>> VersionInfo.parse('3.4.5-pre.2+build.4') + VersionInfo(major=3, minor=4, patch=5, \ +prerelease='pre.2', build='build.4') + + :param version: version string + :return: a :class:`VersionInfo` instance + :rtype: :class:`VersionInfo` + """ + return parse_version_info(version) def _to_dict(obj): @@ -127,6 +193,19 @@ def parse_version_info(version): :param version: version string :return: a :class:`VersionInfo` instance :rtype: :class:`VersionInfo` + + >>> import semver + >>> version_info = semver.parse_version_info("3.4.5-pre.2+build.4") + >>> version_info.major + 3 + >>> version_info.minor + 4 + >>> version_info.patch + 5 + >>> version_info.prerelease + 'pre.2' + >>> version_info.build + 'build.4' """ parts = parse(version) version_info = VersionInfo( @@ -190,6 +269,14 @@ def compare(ver1, ver2): :return: The return value is negative if ver1 < ver2, zero if ver1 == ver2 and strictly positive if ver1 > ver2 :rtype: int + + >>> import semver + >>> semver.compare("1.0.0", "2.0.0") + -1 + >>> semver.compare("2.0.0", "1.0.0") + 1 + >>> semver.compare("2.0.0", "2.0.0") + 0 """ v1, v2 = parse(ver1), parse(ver2) @@ -210,6 +297,12 @@ def match(version, match_expr): != not equal :return: True if the expression matches the version, otherwise False :rtype: bool + + >>> import semver + >>> semver.match("2.0.0", ">=1.0.0") + True + >>> semver.match("1.0.0", ">1.0.0") + False """ prefix = match_expr[:2] if prefix in ('>=', '<=', '==', '!='): @@ -245,6 +338,10 @@ def max_ver(ver1, ver2): :param ver2: version string 2 :return: the greater version of the two :rtype: :class:`VersionInfo` + + >>> import semver + >>> semver.max_ver("1.0.0", "2.0.0") + '2.0.0' """ cmp_res = compare(ver1, ver2) if cmp_res == 0 or cmp_res == 1: @@ -260,6 +357,10 @@ def min_ver(ver1, ver2): :param ver2: version string 2 :return: the smaller version of the two :rtype: :class:`VersionInfo` + + >>> import semver + >>> semver.min_ver("1.0.0", "2.0.0") + '1.0.0' """ cmp_res = compare(ver1, ver2) if cmp_res == 0 or cmp_res == -1: @@ -278,6 +379,10 @@ def format_version(major, minor, patch, prerelease=None, build=None): :param str build: the optional build part of a version :return: the formatted string :rtype: str + + >>> import semver + >>> semver.format_version(3, 4, 5, 'pre.2', 'build.4') + '3.4.5-pre.2+build.4' """ version = "%d.%d.%d" % (major, minor, patch) if prerelease is not None: @@ -308,6 +413,10 @@ def bump_major(version): :param: version string :return: the raised version string :rtype: str + + >>> import semver + >>> semver.bump_major("3.4.5") + '4.0.0' """ verinfo = parse(version) return format_version(verinfo['major'] + 1, 0, 0) @@ -319,6 +428,10 @@ def bump_minor(version): :param: version string :return: the raised version string :rtype: str + + >>> import semver + >>> semver.bump_minor("3.4.5") + '3.5.0' """ verinfo = parse(version) return format_version(verinfo['major'], verinfo['minor'] + 1, 0) @@ -330,6 +443,10 @@ def bump_patch(version): :param: version string :return: the raised version string :rtype: str + + >>> import semver + >>> semver.bump_patch("3.4.5") + '3.4.6' """ verinfo = parse(version) return format_version(verinfo['major'], verinfo['minor'], @@ -343,6 +460,9 @@ def bump_prerelease(version, token='rc'): :param token: defaults to 'rc' :return: the raised version string :rtype: str + + >>> bump_prerelease('3.4.5', 'dev') + '3.4.5-dev.1' """ verinfo = parse(version) verinfo['prerelease'] = _increment_string( @@ -359,6 +479,9 @@ def bump_build(version, token='build'): :param token: defaults to 'build' :return: the raised version string :rtype: str + + >>> bump_build('3.4.5-rc.1+build.9') + '3.4.5-rc.1+build.10' """ verinfo = parse(version) verinfo['build'] = _increment_string( @@ -374,6 +497,14 @@ def finalize_version(version): :param version: version string :return: the finalized version string :rtype: str + + >>> finalize_version('1.2.3-rc.5') + '1.2.3' """ verinfo = parse(version) return format_version(verinfo['major'], verinfo['minor'], verinfo['patch']) + + +if __name__ == "__main__": + import doctest + doctest.testmod()