diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..7efcbf0 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,2 @@ +[run] +omit = venv/* diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml new file mode 100644 index 0000000..4bc2280 --- /dev/null +++ b/.github/workflows/mypy.yml @@ -0,0 +1,20 @@ +name: mypy + +on: + pull_request: + push: + branches: [ master ] + +jobs: + mypy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[dev] + - name: Run mypy + run: | + mypy . diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..8aa88da --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,14 @@ +name: pre-commit + +on: + pull_request: + push: + branches: [ master ] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - uses: pre-commit/action@v2.0.3 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..d731e46 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,27 @@ +name: tests + +on: + pull_request: + push: + branches: [ master ] + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ 3.6, 3.7, 3.8, 3.9 ] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[dev] + - name: Test with pytest + run: | + pytest --cov-report term-missing --cov-config=.coveragerc --cov=pipwhip diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bee0526 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.egg-info +*.pyc +*.coverage +/.env +/.idea +/.mypy_cache +/.pytest_cache +/venv* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..bc25440 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,32 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: mixed-line-ending + - id: check-merge-conflict + - repo: https://github.com/asottile/reorder_python_imports + rev: v2.5.0 + hooks: + - id: reorder-python-imports + args: [ --py36-plus ] + - repo: https://github.com/asottile/pyupgrade + rev: v2.19.0 + hooks: + - id: pyupgrade + args: [ --py36-plus ] + - repo: https://github.com/asottile/add-trailing-comma + rev: v2.1.0 + hooks: + - id: add-trailing-comma + - repo: https://github.com/psf/black + rev: 21.5b2 + hooks: + - id: black + args: [ --line-length=120 ] +ci: + autofix_prs: true + autoupdate_schedule: weekly + skip: [] + submodules: false diff --git a/pipwhip/__init__.py b/pipwhip/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pipwhip/main.py b/pipwhip/main.py new file mode 100644 index 0000000..70d071b --- /dev/null +++ b/pipwhip/main.py @@ -0,0 +1,50 @@ +import click +import httpx +from pkg_resources import parse_requirements +from pkg_resources import parse_version +from pkg_resources import Requirement + + +@click.command() +@click.option( + "-r", + "--requirement", + type=click.Path(), +) +def pipwhip(requirement: str) -> None: + execute(requirement) + + +def get_versions(package_name: str) -> tuple[str, list[str]]: + request = httpx.get(f"https://pypi.org/pypi/{package_name}/json") + + _version = request.json()["releases"] + _name = request.json()["info"]["name"] + _sorted_version = sorted(_version, key=parse_version, reverse=True) + return _name, _sorted_version + + +def file_parse(requirements_file: str) -> list[Requirement]: + with open(requirements_file) as requirements_txt: + return [requirement for requirement in parse_requirements(requirements_txt)] + + +def update_txt_file(requirements_file: str, package: str, version: str) -> None: + with open(requirements_file, "a") as f: + f.write(f"{package}=={version}") + f.write("\n") + + +def erase_file_contents(requirements_file: str) -> None: + with open(requirements_file, "r+") as f: + f.truncate(0) + f.close() + + +def execute(requirements_file: str) -> None: + parsed_file = file_parse(requirements_file) + erase_file_contents(requirements_file) + for package in parsed_file: + result = get_versions(package.name) # type: ignore + latest_version = result[1][0] + update_txt_file(requirements_file, result[0], latest_version) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..ba8c1c8 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,52 @@ +[metadata] +name = pipwhip +version = 0.0.1 +description = A tool to automatically upgrade requirements files. +url = https://github.com/ArcLightSlavik +author = ArcLightSlavik +classifiers = + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + +[options] +install_requires = + click==8.0.1 + httpx==0.18.1 +python_requires = >=3.6.1 + +[options.extras_require] +DEV = + pytest==6.2.4 + pytest-cov==2.12.1 + pytest-mock==3.6.1 + mypy==0.910 + pre-commit==2.13.0 + + # types + types-pkg-resources==0.1.3 + +[options.entry_points] +console_scripts = + pipwhip = pipwhip.main:pipwhip + +[bdist_wheel] +universal = True + +[mypy] +disallow_any_generics = True +disallow_subclassing_any = True +disallow_untyped_calls = True +disallow_untyped_defs = True +disallow_incomplete_defs = True +check_untyped_defs = True +disallow_untyped_decorators = True +no_implicit_optional = True +warn_redundant_casts = True +warn_unused_ignores = True +warn_return_any = True +implicit_reexport = False +strict_equality = True diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0936043 --- /dev/null +++ b/setup.py @@ -0,0 +1,3 @@ +from setuptools import setup # type: ignore + +setup() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/main_test.py b/tests/main_test.py new file mode 100644 index 0000000..8f33030 --- /dev/null +++ b/tests/main_test.py @@ -0,0 +1,42 @@ +from click.testing import CliRunner +from pytest_mock import MockerFixture + +from pipwhip.main import pipwhip + + +def test_execute_fake_package(mocker: MockerFixture) -> None: + mocker.patch( + "pipwhip.main.get_versions", + return_value=( + "pipwhip_test_package_one", + ["0.0.2", "0.0.1"], + ), + ) + + runner = CliRunner() + + with runner.isolated_filesystem(): + with open("requirements_isolated.txt", "w") as f: + f.write("pipwhip_test_package_one==0.0.1") + + runner.invoke(pipwhip, ["-r", "requirements_isolated.txt"]) + + with open("requirements_isolated.txt") as f: + result = f.read() + + assert result.strip() == "pipwhip_test_package_one==0.0.2" + + +def test_execute_real_package() -> None: + runner = CliRunner() + + with runner.isolated_filesystem(): + with open("requirements_isolated.txt", "w") as f: + f.write("recruiter-utils==0.0.1") + + runner.invoke(pipwhip, ["-r", "requirements_isolated.txt"]) + + with open("requirements_isolated.txt") as f: + result = f.read() + + assert result.strip() == "recruiter-utils==0.0.3" diff --git a/tests/test_dir/requirements_test.txt b/tests/test_dir/requirements_test.txt new file mode 100644 index 0000000..523334f --- /dev/null +++ b/tests/test_dir/requirements_test.txt @@ -0,0 +1,2 @@ +pytest==6.2.3 +pytest-cov==2.12.1