diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..90f58843 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +tests: + pytest test_pipenv.py diff --git a/Pipfile b/Pipfile index 2726957c..96f90d4f 100644 --- a/Pipfile +++ b/Pipfile @@ -3,7 +3,10 @@ url = "https://pypi.org/" verify_ssl = true [packages] -requests = "*" crayons = "*" -"delegator.py" = "*" click = "*" +"delegator.py" = "*" + +[develop] +requests = "*" +"requests==2.12.5" = "*" diff --git a/Pipfile.freeze b/Pipfile.freeze index 034e7d3f..b663b768 100644 --- a/Pipfile.freeze +++ b/Pipfile.freeze @@ -1 +1 @@ -{"_meta": {"sources": [{"url": "https://pypi.org/", "verify_ssl": true}], "requires": {}, "Pipfile-sha256": "f752128d0d355349e254f958151439c6d9c38b0b91e0579c6d5095ac45b3d102"}, "default": {"crayons": "*", "click": "*", "delegator.py": "*"}, "develop": {}} \ No newline at end of file +{"_meta": {"sources": [{"url": "https://pypi.org/", "verify_ssl": true}], "requires": {}, "Pipfile-sha256": "6bcf60685687f138fd96fe280157eae89959ae8ee78a5fec71a10293694ecd9e"}, "default": {"crayons": "*", "click": "*", "delegator.py": "*"}, "develop": {}} \ No newline at end of file diff --git a/pipenv.py b/pipenv.py index ea459443..89c79e3c 100644 --- a/pipenv.py +++ b/pipenv.py @@ -29,6 +29,16 @@ def ensure_virtualenv(): pass +def format_toml(data): + """Pretty-formats a given toml string.""" + data = data.split('\n') + for i, line in enumerate(data): + if i > 0: + if line.startswith('['): + data[i] = '\n{}'.format(line) + + return '\n'.join(data) + def add_package_to_pipfile(package_name, dev=False): pipfile_path = pipfile.Pipfile.find() @@ -37,35 +47,81 @@ def add_package_to_pipfile(package_name, dev=False): p = toml.loads(f.read()) key = 'develop' if dev else 'packages' + + # Set empty group if it doesn't exist yet. + if key not in p: + p[key] = {} + + # Add the package to the group. if package_name not in p[key]: # TODO: Support >1.0.1 p[key][package_name] = '*' # Write Pipfile. - - data = toml.dumps(p).split('\n') - for i, line in enumerate(data): - if i > 0: - if line.startswith('['): - data[i] = '\n{}'.format(line) - - data = '\n'.join(data) - + data = format_toml(toml.dumps(p)) with open(pipfile_path, 'w') as f: f.write(data) +def remove_package_from_pipfile(package_name, dev=False): + pipfile_path = pipfile.Pipfile.find() + + # Read and append Pipfile. + with open(pipfile_path, 'r') as f: + p = toml.loads(f.read()) + + key = 'develop' if dev else 'packages' + if package_name in p[key]: + del p[key][package_name] + + # Write Pipfile. + data = format_toml(toml.dumps(p)) + with open(pipfile_path, 'w') as f: + f.write(data) + +def convert_deps_from_pip(dep): + + # requests + # requests==2.12.5 + + return dependencies + + +def convert_deps_to_pip(deps): + dependencies = [] + + for dep in deps.keys(): + # Default (e.g. '>1.10'). + extra = deps[dep] + + # Get rid of '*'. + if deps[dep] == '*' or str(extra) == '{}': + extra = '' + + # Support for extras (e.g. requests[socks]) + if 'extras' in deps[dep]: + extra = '[{}]'.format(deps[dep]['extras'][0]) + + # Support for git. + if 'git' in deps[dep]: + extra = 'git+{}'.format(deps[dep]['git']) + + # Support for @refs. + if 'ref' in deps[dep]: + extra += '@{}'.format(deps[dep]['ref']) + + # Support for editable. + if 'editable' in deps[dep]: + # Support for --egg. + extra += ' --egg={}'.format(dep) + dep = '-e ' + + dependencies.append('{}{}'.format(dep, extra)) + + return dependencies -@click.group() -# @click.option('--version', is_flag=True, callback=display_version, help='Display version information') -@click.version_option(prog_name=crayons.yellow('pip2'), version=__version__) -def cli(*args, **kwargs): - # Ensure that pip is installed and up-to-date. - ensure_latest_pip() - # Ensure that virtualenv is installed. - ensure_virtualenv() def virtualenv_location(): return os.sep.join(pipfile.Pipfile.find().split(os.sep)[:-1] + ['.venv']) @@ -98,27 +154,18 @@ def do_where(virtualenv=False, bare=True): click.echo(location) -def convert_deps(deps): - dependencies = [] - for dep in deps.keys(): - # Default (e.g. '>1.10'). - extra = deps[dep] - # Get rid of '*'. - if extra == '*': - extra = '' +@click.group() +# @click.option('--version', is_flag=True, callback=display_version, help='Display version information') +@click.version_option(prog_name=crayons.yellow('pip2'), version=__version__) +def cli(*args, **kwargs): + # Ensure that pip is installed and up-to-date. + ensure_latest_pip() - # Support for extras (e.g. requests[socks]) - if 'extras' in extra: - extra = '[{}]'.format(deps[dep]['extras'][0]) + # Ensure that virtualenv is installed. + ensure_virtualenv() - # Support for git. - # if 'editablle' - - dependencies.append('{} {}'.format(dep, extra)) - - return dependencies def which_pip(): return os.sep.join([virtualenv_location()] + ['bin/pip']) @@ -149,6 +196,9 @@ def prepare(dev=False): click.echo(crayons.red('Pipfile.freeze out of date, updating...')) # Update the lockfile. + # TODO: Add sub-dependencies. + with open(lockfile_location(), 'w') as f: + f.write(p.freeze()) else: @@ -165,7 +215,7 @@ def prepare(dev=False): deps.update(lockfile['develop']) # Convert the deps to pip-compatbile arguments. - deps = convert_deps(deps) + deps = convert_deps_to_pip(deps) # Actually install each dependency into the virtualenv. for package_name in deps: @@ -207,6 +257,12 @@ def install(package_name, dev=False): def uninstall(package_name): click.echo('Un-installing {}...'.format(crayons.green(package_name))) + c = delegator.run('{} uninstall {} -y'.format(which_pip(), package_name)) + click.echo(crayons.blue(c.out)) + + click.echo('Removing {} from Pipfile...'.format(crayons.green(package_name))) + remove_package_from_pipfile(package_name) + # Install click commands. cli.add_command(prepare) cli.add_command(where) diff --git a/test_pipenv.py b/test_pipenv.py new file mode 100644 index 00000000..ddac7a2a --- /dev/null +++ b/test_pipenv.py @@ -0,0 +1,51 @@ +import pytest + +import pipenv + + + +def test_convert_deps_to_pip(): + + # requests = '*' + deps = {'requests': '*'} + deps = pipenv.convert_deps_to_pip(deps) + assert deps[0] == 'requests' + + # requests = {} + deps = {'requests': {}} + deps = pipenv.convert_deps_to_pip(deps) + assert deps[0] == 'requests' + + # requests = { extras = ['socks'] } + deps = {'requests': {'extras': ['socks']}} + deps = pipenv.convert_deps_to_pip(deps) + assert deps[0] == 'requests[socks]' + + # Django = '>1.10' + deps = {'django': '>1.10'} + deps = pipenv.convert_deps_to_pip(deps) + assert deps[0] == 'django>1.10' + + # pinax = { git = 'git://github.com/pinax/pinax.git', ref = '1.4', editable = true } + deps = {'pinax': {'git': 'git://github.com/pinax/pinax.git', 'ref': '1.4', 'editable': True}} + deps = pipenv.convert_deps_to_pip(deps) + assert deps[0] == '-e git+git://github.com/pinax/pinax.git@1.4 --egg=pinax' + + +def test_convert_from_pip(): + + # requests + + # requests[socks] + + # Django>1.10 + # deps = pipenv.convert_deps_to_pip(deps) + # assert deps[0] == 'django>1.10' + + # -e git+git://github.com/pinax/pinax.git@1.4 + # deps = {'pinax': {'git': 'git://github.com/pinax/pinax.git', 'ref': '1.4', 'editable': True}} + # deps = pipenv.convert_deps_to_pip(deps) + # assert deps[0] == '-e git+git://github.com/pinax/pinax.git@1.4 --egg=pinax' + pass + +