Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Madoshakalaka committed Nov 17, 2019
2 parents dc3ac17 + 5ba4275 commit 889dd2d
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 75 deletions.
137 changes: 84 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,41 @@
# Pipenv-Setup

[![travis-badge](https://travis-ci.org/Madoshakalaka/pipenv-setup.svg?branch=master)](https://travis-ci.org/Madoshakalaka/pipenv-setup)
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/pipenv-setup.svg)](https://pypi.python.org/pypi/pipenv-setup/)
[![codecov](https://codecov.io/gh/Madoshakalaka/pipenv-setup/branch/master/graph/badge.svg)](https://codecov.io/gh/Madoshakalaka/pipenv-setup)
[![PyPI version](https://badge.fury.io/py/pipenv-setup.svg)](https://badge.fury.io/py/pipenv-setup)
[![Very popular](https://img.shields.io/pypi/dm/pipenv-setup)](https://pypistats.org/packages/pipenv-setup)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

Sync dependencies in Pipfile or Lockfile to setup.py
A beautiful python package development tool: sync dependencies in `Pipfile` or `Pipfile.lock` to `setup.py`.

Never need again to change dependencies manually in `setup.py`, and enjoy the same
dependency locking or semantic versioning.

A beautiful python package development tool.
Or just check whether `setup.py` and `Pipfile` are consistent and sync dependency when
necessary.

Never need again to change dependencies
manually in `setup.py` and enjoy the same
dependency locking or semantic versioning

Or just check whether setup.py and pipfile are consistent and sync dependency when necessary
### Install
## Installation

`$ pipenv install --dev pipenv-setup`
Create a command line entry point `pipenv-setup`, and add `pipenv-setup` as a `dev`
package in `Pipfile`:

it creates command line entry `$ pipenv-setup`
```bash
pipenv install --dev pipenv-setup
```

## Features

### Beautiful pipenv flavored help

`$ pipenv-setup`

![help](https://raw.githubusercontent.com/Madoshakalaka/pipenv-setup/master/readme_assets/help.PNG)

### Sync to `setup.py`

- supports assorted package configuration. You can have a pipfile as ugly as you want:

```Pipfile
[package]
requests = { extras = ['socks'] }
Expand All @@ -39,17 +45,21 @@ it creates command line entry `$ pipenv-setup`
"e1839a8" = {path = ".", editable = true}
pywinusb = { version = "*", os_name = "=='nt'", index="pypi"}
```
`pipenv-setup` will still figure things out:
`$ pipenv-setup sync`
```
```bash
$ pipenv-setup sync
package e1839a8 is local, omitted in setup.py
setup.py successfully updated
23 packages from Pipfile.lock synced to setup.py
```
And things will be where they should be
And things will be where they should be:
```python
# setup.py
setup(...,
install_requires=[
"certifi==2017.7.27.1",
"chardet==3.0.4",
Expand All @@ -62,47 +72,70 @@ it creates command line entry `$ pipenv-setup`
"git+https://github.com/django/[email protected]#egg=django",
"https://github.com/divio/django-cms/archive/release/3.4.x.zip",
],
)
```
- provide `--dev` flag to sync development packages to `extras_require`
```
- provide `--dev` flag to sync development packages with `extras_require`:
```bash
$ pipenv-setup sync --dev
setup.py successfully updated
1 default packages from Pipfile.lock synced to setup.py
1 dev packages from Pipfile.lock synced to setup.py
```
```
# produced setup.py
extras_require={"dev": ["pytest==1.1.3",]},
install_requires=["xml-subsetter==0.0.1"],
```python
# produced setup.py
setup(...,
extras_require={"dev": ["pytest==1.1.3",]},
install_requires=["xml-subsetter==0.0.1"],
)
```
- provide `--pipfile` flag to sync Pipfile instead of lockfile, which syncs semantic versioning in pipfile instead of pinned versions in lockfile.
- Beautiful [Blackened](https://github.com/psf/black) setup.py file.
- [Template](https://github.com/pypa/sampleproject/blob/master/setup.py) generation with filled dependencies in the absence of a setup file.
`$ pipenv-setup sync`
```
- produce beautiful [Blackened](https://github.com/psf/black) `setup.py` file
- [Template](https://github.com/pypa/sampleproject/blob/master/setup.py) generation with
filled dependencies in the absence of a setup file.
```bash
$ pipenv-setup sync
setup.py not found under current directory
Creating boilerplate setup.py...
setup.py successfully generated under current directory
23 packages moved from Pipfile.lock to setup.py
setup.py was successfully generated
23 packages synced from Pipfile.lock to setup.py
Please edit the required fields in the generated file
```
> Note: by default $ pipenv-setup syncs lockfile instead of pipfile
> Note: by default, `pipenv-setup` syncs lockfile instead of pipfile
#### Sync `Pipfile` vs. `Pipfile.lock`
Provide `--pipfile` flag to sync `Pipfile` instead of `Pipfile.lock`.
`pipenv-setup`
will perform a liquid sync using semantic versioning taken from `Pipfile` (instead of
using frozen pinned versions from `Pipfile.lock`):
```bash
$ pipenv-setup sync --pipfile
setup.py was successfully updated
23 packages synced from Pipfile to setup.py
```

### Checks Only

run `$ pipenv-setup check`

- checks four items
- local package in default pipfile packages
- Package version requirements in `install_requires` in setup.py that potentially violates Pipfile
- Package version requirements in `dependency_links` in setup.py that differs from Pipfile
- Default package in pipfile missing in `install_requires` or `dependency_links` in setup.py
- local package in default pipfile packages
- Package version requirements in `install_requires` in setup.py that potentially violates Pipfile
- Package version requirements in `dependency_links` in setup.py that differs from Pipfile
- Default package in pipfile missing in `install_requires` or `dependency_links` in setup.py
- exits with non-zero code when conflict found (can be used in travis-ci)
- here is a somewhat extreme example
```
- here is a somewhat extreme example:

```bash
$ pipenv-setup check
package 'numpy' has version string: >=1.2 in setup.py, which potentially violates >=1.5 in pipfile
package 'pywinusb' has version string: ==0.4.2 in setup.py, which is disjoint from ~=0.3.0 in pipfile
package 'records' has version string: >=0.4.2,<0.5 in setup.py, which is disjoint from >0.5.0 in pipfile
Expand All @@ -112,36 +145,34 @@ run `$ pipenv-setup check`
(exits with 1)
```

- provide `--ignore-local` flag to allow local packages in pipfile
```

```bash
$ pipenv-setup check
local package found in default dependency: e1839a8.
Do you mean to make it dev dependency
Do you mean to make it dev dependency
(exits with 1)
```

```
```bash
$ pipenv-setup check --ignore-local
No version conflict or missing packages/dependencies found in setup.py!
(exits with 0)
```

- provide `--strict` flag to only pass identical version requirements

By default `pipenv-setup check` passes when the version `setup.py` specifies is "compatible" with `Pipfile`, i.e. is a subset of it.
By default `pipenv-setup check` passes when the version `setup.py` specifies is
"compatible" with `Pipfile`, i.e. is a subset of it. For example, a Pipfile
specifying `django~=1.1` with `setup.py` requiring `django==1.2` is such a case.

Provide `--strict` to allow only identical requirements; *i.e.* for `Pipfile`'s
`django~=1.1`, `setup.py` must require `django>=1.1,<2.0`
For example, `pipfile`: django~=1.1 `setup.py`: django==1.2 is such a case.
provide `--strict` to allow only identical requirements such as `pipfile`: django~=1.1 `setup.py`: django>=1.1,<2.0
Example output:
```
pipenv-setup check --strict
```bash
$ pipenv-setup check --strict
package 'pywinusb' has version string: ==0.4.2 in setup.py, which specifies a subset of * in pipfile
package 'django' has version string: >=0.5 in setup.py, which is disjoint from ~=0.3.0 in pipfile
package 'records' has version string: ==0.5.2 in setup.py, which specifies a subset of >0.5.0 in pipfile
Expand All @@ -151,4 +182,4 @@ run `$ pipenv-setup check`
## Contributing
If you'd like to contribute to `pipenv-setup`. See [Contribution Guide](CONTRIBUTING.md)
If you'd like to contribute to `pipenv-setup`, see [Contribution Guide](CONTRIBUTING.md)
19 changes: 10 additions & 9 deletions pipenv_setup/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,21 +248,20 @@ def sync(argv):
print("Creating boilerplate setup.py...")
setup_code = setup_filler.fill_boilerplate(dependency_arguments)
if setup_code is None:
fatal_error("Can not find setup.py template file")
fatal_error("Cannot find setup.py template file")
try:
with open(setup_file_path, "w") as new_setup_file:
with open(str(setup_file_path), "w") as new_setup_file:
new_setup_file.write(setup_code)
format_file(setup_file_path)
except OSError as e:
fatal_error([str(e), "failed to write setup.py file"])
else:
congratulate(
[
"setup.py generated",
"%d packages moved from %s to setup.py"
% (default_package_success_count, file.name),
"Please edit the required fields in the generated file",
]
msg_formatter.generate_success(
default_package_success_count,
dev_package_success_count,
argv.pipfile,
)
)

else: # all files exist. Update setup.py
Expand All @@ -274,7 +273,9 @@ def sync(argv):
fatal_error([str(e), msg_formatter.no_sync_performed()])
congratulate(
msg_formatter.update_success(
default_package_success_count, dev_package_success_count
default_package_success_count,
dev_package_success_count,
argv.pipfile,
)
)
else:
Expand Down
42 changes: 34 additions & 8 deletions pipenv_setup/msg_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,47 @@ def checked_no_problem():
return "No version conflict or missing packages/dependencies found in setup.py!"


def generate_success(
default_package_count, dev_package_count=0, pipfile=False
): # type: (int, int, bool) -> str
"""
:param default_package_count: The number of updated default packages
:param dev_package_count: The number of updated dev packages
:param bool lockfile: indicate that Pipfile was used to update setup.py
"""
src = "Pipfile" if pipfile else "Pipfile.lock"
string = (
"setup.py was successfully generated"
"\n%d default packages synced from %s to setup.py" % (default_package_count, src)
)

if dev_package_count != 0:
string += "\n%d dev packages from %s synced to setup.py" % (
default_package_count,
src,
)

string += "\nPlease edit the required fields in the generated file"
return string

def update_success(
default_package_count, dev_package_count=0
): # type: (int, int) -> str
default_package_count, dev_package_count=0, pipfile=False
): # type: (int, int, bool) -> str
"""
:param default_package_count: The number of updated default packages
:param dev_package_count: The number of updated dev packages
:param bool lockfile: indicate that Pipfile was used to update setup.py
"""
src = "Pipfile" if pipfile else "Pipfile.lock"
string = (
"setup.py successfully updated"
+ "\n%d default packages from Pipfile.lock synced to setup.py"
% default_package_count
"setup.py was successfully updated"
+ "\n%d default packages from %s synced to setup.py"
% (default_package_count, src)
)

if dev_package_count != 0:
string += (
"\n%d dev packages from Pipfile.lock synced to setup.py"
% default_package_count
string += "\n%d dev packages from %s synced to setup.py" % (
default_package_count,
src,
)
return string
31 changes: 31 additions & 0 deletions tests/main_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,37 @@ def test_update(
assert msg_formatter.update_success(update_count) in captured.out


@pytest.mark.parametrize(
("source_pipfile_dirname", "update_count"),
[("nasty_0", 23), ("no_original_kws_0", 23)],
)
def test_only_setup_missing(
capsys, tmp_path, shared_datadir, source_pipfile_dirname, update_count
): # type: (Any, Path, Path, str, int) -> None
"""
test generate setup.py (when it is missing)
"""
pipfile_dir = shared_datadir / source_pipfile_dirname
with data(source_pipfile_dirname, tmp_path):
# delete the setup.py file that was copied to the tmp_path
(tmp_path / "setup.py").unlink()
cmd(argv=["", "sync"])
generated_setup = Path("setup.py")
assert generated_setup.exists()
generated_setup_text = generated_setup.read_text()
expected_setup_text = Path("setup.py").read_text()
for kw_arg_names in ("install_requires", "dependency_links"):

assert compare_list_of_string_kw_arg(
generated_setup_text,
expected_setup_text,
kw_arg_names,
ordering_matters=False,
)
captured = capsys.readouterr()
assert msg_formatter.generate_success(update_count) in captured.out, captured.out


@pytest.mark.parametrize(("source_pipfile_dirname",), [("nasty_0",)])
@pytest.mark.parametrize(
("missing_filenames",),
Expand Down
Loading

0 comments on commit 889dd2d

Please sign in to comment.