Last active
August 25, 2019 14:41
-
-
Save iddan/f190c3c7d54f4fc4655da95fb185e641 to your computer and use it in GitHub Desktop.
Sync Pipfile and setup.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Sync Pipfile with setup.py dependencies | |
# Assumptions: | |
# - You are running in a directory with Pipfile, Pipfile.lock & setup.py | |
# - Your setup.py calls a function named setup() | |
# - setup() is called with keyword arguments of install_requires and dependency_links (can be empty lists) | |
# - All your remote dependencies are HTTPS git | |
import pipfile | |
import ast | |
import json | |
import astunparse | |
from yapf.yapflib import yapf_api | |
from typing import Union | |
pipfile_ = pipfile.load() | |
with open('Pipfile.lock') as file: | |
pipfile_lock = json.load(file) | |
def format_str_config(name: str, config: str) -> str: | |
name = name.lower() | |
if name not in pipfile_lock['default'] and name.replace( | |
'_', '-') in pipfile_lock['default']: | |
name = name.replace('_', '-') | |
if config == '*': | |
return f'{pipfile_lock["default"][name]["version"]}' | |
return config | |
def format_dict_config(name: str, config: dict) -> str: | |
formatted = '' | |
for key, value in config.items(): | |
if key == 'version': | |
formatted += format_str_config(name, value) | |
if key == 'os_name': | |
formatted += f'; os_name{value}' | |
return formatted | |
def is_editable(config: Union[str, dict]) -> bool: | |
return isinstance(config, dict) and 'editable' in config | |
class UpdateInstallRequires(ast.NodeTransformer): | |
def visit_Call(self, node): | |
# TODO stronger check | |
if node.func.id == 'setup': | |
for keyword in node.keywords: | |
if keyword.arg == 'install_requires': | |
install_requires = keyword.value | |
if keyword.arg == 'dependency_links': | |
dependency_links = keyword.value | |
dependency_links.elts = [ | |
ast.Str(f'{config["git"]}#egg={name}') | |
for name, config in pipfile_.data['default'].items() | |
if is_editable(config) | |
] | |
install_requires.elts = [ | |
ast.Str( | |
f'{name}{format_str_config(name, config) if isinstance(config, str) else format_dict_config(name, config)}' | |
) if not is_editable(config) else ast.Str(name) | |
for name, config in pipfile_.data['default'].items() | |
] | |
return node | |
with open('setup.py') as file: | |
tree = ast.parse(file.read()) | |
tree = UpdateInstallRequires().visit(tree) | |
source = astunparse.unparse(tree) | |
reformatted_source, _ = yapf_api.FormatCode(source) | |
with open('setup.py', 'w+') as file: | |
file.write(reformatted_source) |
Hi! I developed a package specifically for this: pipenv-setup. which does the sync on one command $ pipenv-setup sync
.
My packages refined the following:
- This script strips all comments away. pipenv-setup keeps them and edit
install_requires
,dependency_links
in-place or create them when they are absent. - More general markers including "os_name" this script has.
- General package config support. For example VCS other than git, built distribution with url.
- Bug fixes. For example this code doesn't work when there exist function calls other than setup()
Black
for formatting
And thanks for your inspiration! It saved me a lot of time!!
@bradwood
The traceback shows that package setuptools
is not in pipfile.lock
maybe just run pipenv update
to make sure it goes in the file
@Madoshakalaka glad it was useful
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I like this script a lot, but I'm getting a KeyError: 'setuptools'... which i suspect is from my
from setuptools import setup
.Any suggestions on how to fix this?
Full stack dump