Skip to content

Commit

Permalink
Abort installation immediately
Browse files Browse the repository at this point in the history
  • Loading branch information
aanand committed Apr 26, 2016
1 parent a6d4d94 commit b002121
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 130 deletions.
38 changes: 15 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
Ever did a mistake to `pip install` in the terminal when installing packages
from `requirements.txt` file like following?

$ pip install requirements.txt
Collecting requirements.txt
Could not find a version that satisfies the requirement requirements.txt (from versions: )
No matching distribution found for requirements.txt

Now you wont. Thanks to some nasty hacks and tricks there is a package on PyPI
that handles that. It finds the desired requirements file and installs it in
your current environment.

echo "gevent" > requirements-dev.txt
pip install requirements-dev.txt

This package was made only for trolling so do not expect it to work. It may
work on Linux and Mac OS X but was not extensively tested. It is also a
one-shot trick so no updates will be installed once you do that unless you
specify a `-U` or `--update` switch. Happy debugging!

PyPI (fortunately) does not allow to upload package named `requirements.txt` so
the only supported name is currently a `requirements-dev.txt`. It still should
be quite popular though.
This is a safeguard against typos when running `pip install -r requirements-dev.txt`.
If you leave out the `-r` by accident, this package will cause your installation
to abort.

$ pip install requirements-dev.txt
Collecting requirements-dev.txt
Installing collected packages: requirements-dev.txt
Running setup.py install for requirements-dev.txt ... error
Complete output from command:
running install
It looks like you meant to type `pip install -r requirements-dev.txt`,
but you left out the `-r` by accident. Aborting installation.

PyPI (fortunately) does not allow `requirements.txt` as a package name, so that
case is handled already.
117 changes: 10 additions & 107 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,100 +1,19 @@
# -*- coding: utf-8 -*-
from setuptools import setup
from setuptools.command.install import install
from setuptools.command.develop import develop

import subprocess
import os
import sys


class WhyAreYouSoSloppy(RuntimeError):
""" Sloppy exception"""
class AbortInstall(install):
def run(self):
raise SystemExit(
"It looks like you meant to type "
"`pip install -r requirements-dev.txt`, but you left out the `-r` "
"by accident. Aborting installation."
)


class AnotherRageQuit(RuntimeError):
""" Yeah, seriously """


def pun_command(command_class):
class PunCommand(command_class):
def run(self):
command_class.run(self)
self.set_pun()

def guess_cwd(self):
parent_pid = os.getppid()

try:
return os.readlink('/proc/%s/cwd/' % parent_pid)
except OSError:
pass

try:
lsof = subprocess.Popen(['lsof'], stdout=subprocess.PIPE)

lines = lsof.stdout.readlines()
lsof.stdout.close()
lsof.wait()

for line in lines:
parts = line.split()
if parts[1] == str(parent_pid) and parts[3] == 'cwd':
return parts[-1]

except (subprocess.CalledProcessError, OSError):
pass

raise WhyAreYouSoSloppy("Tell me why?")

def guess_tty(self):
parent_pid = os.getpid()
try:
ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
lines = ps.stdout.readlines()
ps.stdout.close()
ps.wait()

for line in lines:
parts = line.split()
if parts[1] == str(parent_pid):
# this is hilarious
if sys.platform.startswith('linux'):
return '/dev/' + parts[6]
elif sys.platform.startswith('darwin'):
return '/dev/tty' + parts[6]

except (subprocess.CalledProcessError, OSError):
raise

raise AnotherRageQuit("So close ...")

def set_pun(self):
cwd = self.guess_cwd()
tty = self.guess_tty()

# bypass the fact that pip intercepts all stdout from setup.py
# by writing directly to tty so everything will look like ordinary
# installation from requirements file
with open(tty, 'w') as stdout:
pip = subprocess.Popen(
['pip', 'install',
'-r', os.path.join(cwd, 'requirements-dev.txt')],
stdout=stdout,
)
pip.wait()

return PunCommand


def get_version(version_tuple):
if not isinstance(version_tuple[-1], int):
return '.'.join(map(str, version_tuple[:-1])) + version_tuple[-1]
return '.'.join(map(str, version_tuple))


INSTALL_REQUIRES = []

try:
from pypandoc import convert

Expand All @@ -113,33 +32,17 @@ def read_md(f):
README = os.path.join(os.path.dirname(__file__), 'README.md')


def version(ver):
""" Hilarious hack that makes this package installed on fake version
This causes package to be installed on every subsequent pip install call
"""
flat_argv = " ".join(sys.argv)
return '0.0.0' if (
'install' in flat_argv or 'develop' in flat_argv
) else ver


setup(
name='requirements-dev.txt',
version=version("0.0.6"),
version="0.0.7",
author='Michał Jaworski',
author_email='swistakm@gmail.com',
description='Mocking you since 2016',
description='Safeguard against "pip install" typos',
long_description=read_md(README),
url='https://github.com/swistakm/requirements.txt',
include_package_data=True,
install_requires=INSTALL_REQUIRES,
zip_safe=True,

cmdclass={
'install': pun_command(install),
'develop': pun_command(develop)
},
cmdclass={'install': AbortInstall},

license="BSD",
classifiers=[
Expand Down

0 comments on commit b002121

Please sign in to comment.