From be4b70e646e6232834e9f9917fdc1adde2156f47 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Thu, 19 Jan 2017 19:44:22 -0500 Subject: [PATCH] blah --- README.rst | 0 delegator.py | 227 +++++++++++++++++++++++++++++++++++++++++++ pip2.py | 32 ++++++ pipfile/__about__.py | 21 ++++ pipfile/__init__.py | 11 +++ pipfile/_json.py | 54 ++++++++++ pipfile/api.py | 196 +++++++++++++++++++++++++++++++++++++ requirements.txt | 5 + 8 files changed, 546 insertions(+) create mode 100644 README.rst create mode 100644 delegator.py create mode 100644 pip2.py create mode 100644 pipfile/__about__.py create mode 100644 pipfile/__init__.py create mode 100644 pipfile/_json.py create mode 100644 pipfile/api.py create mode 100644 requirements.txt diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..e69de29b diff --git a/delegator.py b/delegator.py new file mode 100644 index 00000000..59222391 --- /dev/null +++ b/delegator.py @@ -0,0 +1,227 @@ +import os +import subprocess +import shlex + +from pexpect.popen_spawn import PopenSpawn +import daemon + + +class Command(object): + def __init__(self, cmd): + super(Command, self).__init__() + self.cmd = cmd + self.subprocess = None + self.blocking = None + self.was_run = False + self.__out = None + + def __repr__(self): + return ''.format(self.cmd) + + @property + def _popen_args(self): + return self.cmd + + @property + def _default_popen_kwargs(self): + return { + 'env': os.environ.copy(), + 'stdin': subprocess.PIPE, + 'stdout': subprocess.PIPE, + 'stderr': subprocess.PIPE, + 'shell': True, + 'universal_newlines': True, + 'bufsize': 0, + } + + @property + def _default_pexpect_kwargs(self): + return { + 'env': os.environ.copy(), + } + + @property + def _uses_subprocess(self): + return isinstance(self.subprocess, subprocess.Popen) + + @property + def _uses_pexpect(self): + return isinstance(self.subprocess, PopenSpawn) + + @property + def std_out(self): + return self.subprocess.stdout + + @property + def _pexpect_out(self): + result = '' + + if self.subprocess.before: + result += self.subprocess.before + + if isinstance(self.subprocess.after, str): + result += self.subprocess.after + + result += self.subprocess.read() + return result + + @property + def out(self): + """Std/out output (cached), as well as stderr for non-blocking runs.""" + if self.__out: + return self.__out + + if self._uses_subprocess: + self.__out = self.std_out.read() + else: + self.__out = self._pexpect_out + + return self.__out + + @property + def std_err(self): + return self.subprocess.stderr + + @property + def err(self): + if self._uses_subprocess: + return self.std_err.read() + else: + return self._pexpect_out + + @property + def pid(self): + """The process' PID.""" + # Support for pexpect's functionality. + if hasattr(self.subprocess, 'proc'): + return self.subprocess.proc.pid + # Standard subprocess method. + return self.subprocess.pid + + @property + def return_code(self): + return self.subprocess.returncode + + @property + def std_in(self): + return self.subprocess.stdin + + def run(self, block=True): + """Runs the given command, with or without pexpect functionality enabled.""" + self.blocking = block + + # Use subprocess. + if self.blocking: + s = subprocess.Popen(self._popen_args, **self._default_popen_kwargs) + + # Otherwise, use pexpect. + else: + s = PopenSpawn(self._popen_args, **self._default_pexpect_kwargs) + self.subprocess = s + self.was_run = True + + def expect(self, pattern, timeout=-1): + """Waits on the given pattern to appear in std_out""" + + if self.blocking: + raise RuntimeError('expect can only be used on non-blocking commands.') + + self.subprocess.expect(pattern=pattern, timeout=timeout) + + def send(self, s, end=os.linesep, signal=False): + """Sends the given string or signal to std_in.""" + + if self.blocking: + raise RuntimeError('send can only be used on non-blocking commands.') + + if not signal: + if self._uses_subprocess: + return self.subprocess.communicate(s + end) + else: + return self.subprocess.send(s + end) + else: + self.subprocess.send_signal(s) + + def terminate(self): + self.subprocess.terminate() + + def kill(self): + self.subprocess.kill() + + def block(self): + """Blocks until process is complete.""" + self.subprocess.wait() + + def daemonize(self): + """Daemonizes a non-blocking process.""" + if self.blocking: + raise RuntimeError('daemonize can only be used on non-blocking commands.') + + with daemon.DaemonContext(): + self.block() + + def pipe(self, command): + """Runs the current command and passes its output to the next + given process. + """ + if not self.was_run: + self.run(block=False) + + data = self.out + + c = Command(command) + c.run(block=False) + if data: + c.send(data) + c.subprocess.sendeof() + c.block() + return c + + +def _expand_args(command): + """Parses command strings and returns a Popen-ready list.""" + + # Prepare arguments. + if isinstance(command, (str, unicode)): + splitter = shlex.shlex(command.encode('utf-8')) + splitter.whitespace = '|' + splitter.whitespace_split = True + command = [] + + while True: + token = splitter.get_token() + if token: + command.append(token) + else: + break + + command = list(map(shlex.split, command)) + + return command + + +def chain(command): + commands = _expand_args(command) + data = None + + for command in commands: + + c = run(command, block=False) + + if data: + c.send(data) + c.subprocess.sendeof() + + data = c.out + + return c + + +def run(command, block=True): + c = Command(command) + c.run(block=block) + + if block: + c.block() + + return c \ No newline at end of file diff --git a/pip2.py b/pip2.py new file mode 100644 index 00000000..e070de9f --- /dev/null +++ b/pip2.py @@ -0,0 +1,32 @@ +import delegator +import click +import crayons + + +def ensure_latest_pip(): + + # Ensure that pip is installed. + c = delegator.run('pip install pip') + + # Check if version is out of date. + if 'however' in c.err: + # If version is out of date, update. + print crayons.yellow('Pip is out of date... updating to latest.') + c = delegator.run('pip install pip --upgrade', block=False) + print crayons.blue(c.out) + +def ensure_virtualenv(): + c = delegator.run('pip install virtualenv') + print c.out + + +@click.command() +def main(): + # Ensure that pip is installed and up-to-date. + ensure_latest_pip() + + # Ensure that virtualenv is installed. + ensure_virtualenv() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/pipfile/__about__.py b/pipfile/__about__.py new file mode 100644 index 00000000..35b15962 --- /dev/null +++ b/pipfile/__about__.py @@ -0,0 +1,21 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] + +__title__ = "pipfile" +__summary__ = "" +__uri__ = "https://github.com/pypa/pipfile" + +__version__ = "16.0.dev0" + +__author__ = "Kenneth Reitz and individual contributors" +__email__ = "me@kennethreitz.org" + +__license__ = "BSD or Apache License, Version 2.0" +__copyright__ = "Copyright 2017 %s" % __author__ \ No newline at end of file diff --git a/pipfile/__init__.py b/pipfile/__init__.py new file mode 100644 index 00000000..4c5b12a7 --- /dev/null +++ b/pipfile/__init__.py @@ -0,0 +1,11 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +from .__about__ import ( + __author__, __copyright__, __email__, __license__, __summary__, __title__, + __uri__, __version__ +) + +from .api import load, Pipfile \ No newline at end of file diff --git a/pipfile/_json.py b/pipfile/_json.py new file mode 100644 index 00000000..a8d00eba --- /dev/null +++ b/pipfile/_json.py @@ -0,0 +1,54 @@ +import _ctypes +import json +import re +from collections import OrderedDict + +# http://stackoverflow.com/questions/13249415/can-i-implement-custom-indentation-for-pretty-printing-in-python-s-json-module +# +def di(obj_id): + # from http://stackoverflow.com/a/15012814/355230 + """ Reverse of id() function. """ + return _ctypes.PyObj_FromPtr(obj_id) + +class NoIndent(object): + def __init__(self, value): + self.value = value + def __repr__(self): + if isinstance(self.value, OrderedDict): + return json.dumps(self.value) + if not isinstance(self.value, list): + return repr(self.value) + else: # the sort the representation of any dicts in the list + reps = ('{{{}}}'.format(', '.join(('{!r}:{}'.format( + k, v) for k, v in sorted(v.items())))) + if isinstance(v, dict) else repr(v) for v in self.value) + + return '[' + ', '.join(reps) + ']' + +class NoIndentEncoder(json.JSONEncoder): + FORMAT_SPEC = "@@{}@@" + regex = re.compile(FORMAT_SPEC.format(r"(\d+)")) + + def default(self, obj): + if not isinstance(obj, NoIndent): + return super(NoIndentEncoder, self).default(obj) + return self.FORMAT_SPEC.format(id(obj)) + + def encode(self, obj): + format_spec = self.FORMAT_SPEC # local var to expedite access + result = super(NoIndentEncoder, self).encode(obj) + for match in self.regex.finditer(result): + id = int(match.group(1)) + result = result.replace('"{}"'.format(format_spec.format(id)), + repr(di(int(id)))) + return result + + +def dumps(obj): + """Returns specific data in a specific format.""" + obj['_meta']['requires'] = [NoIndent(i) for i in obj['_meta']['requires']] + obj['_meta']['sources'] = [NoIndent(i) for i in obj['_meta']['sources']] + obj['default'] = [NoIndent(i) for i in obj['default']] + obj['develop'] = [NoIndent(i) for i in obj['develop']] + + return json.dumps(obj, sort_keys=True, cls=NoIndentEncoder, indent=4, separators=(',', ': ')) \ No newline at end of file diff --git a/pipfile/api.py b/pipfile/api.py new file mode 100644 index 00000000..a475dad7 --- /dev/null +++ b/pipfile/api.py @@ -0,0 +1,196 @@ +import toml + +import json +import hashlib +import platform +import sys +import os +from collections import OrderedDict + +from . import _json + +def format_full_version(info): + version = '{0.major}.{0.minor}.{0.micro}'.format(info) + kind = info.releaselevel + if kind != 'final': + version += kind[0] + str(info.serial) + return version + + +def walk_up(bottom): + """mimic os.walk, but walk 'up' instead of down the directory tree. + From: https://gist.github.com/zdavkeos/1098474 + """ + + bottom = os.path.realpath(bottom) + + # get files in current dir + try: + names = os.listdir(bottom) + except Exception as e: + print e + return + + dirs, nondirs = [], [] + for name in names: + if os.path.isdir(os.path.join(bottom, name)): + dirs.append(name) + else: + nondirs.append(name) + + yield bottom, dirs, nondirs + + new_path = os.path.realpath(os.path.join(bottom, '..')) + + # see if we are at the top + if new_path == bottom: + return + + for x in walk_up(new_path): + yield x + + + +class PipfileParser(object): + def __init__(self, filename='Pipfile'): + self.filename = filename + self.sources = [] + self.groups = OrderedDict({ + 'default': [], + 'develop': [] + }) + self.group_stack = ['default'] + self.requirements = [] + + def __repr__(self): + return '