diff --git a/pyproject.toml b/pyproject.toml index 2173ae3..cc3d59f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "poetryup" -version = "0.6.2" +version = "0.6.3" description = "Update dependencies and bump their version in the pyproject.toml file" authors = ["Mousa Zeid Baker"] packages = [ diff --git a/src/poetryup/pyproject.py b/src/poetryup/core/pyproject.py similarity index 64% rename from src/poetryup/pyproject.py rename to src/poetryup/core/pyproject.py index aafd701..3ea5d8d 100644 --- a/src/poetryup/pyproject.py +++ b/src/poetryup/core/pyproject.py @@ -1,36 +1,12 @@ import logging import re import subprocess -from dataclasses import dataclass -from typing import List, Optional, Union +from typing import Dict, List, Optional, Union import tomlkit from packaging import version as version_ -from tomlkit import items - -@dataclass -class Dependency: - """A class to represent a dependency""" - - name: str - version: Union[items.String, items.InlineTable, items.Array] - group: str - - @property - def normalized_name(self) -> str: - # https://www.python.org/dev/peps/pep-0503/#normalized-names - return self.name.replace("_", "-").lower() - - @property - def constraint(self) -> str: - if type(self.version) is items.String: - if self.version[0].startswith(("^", "~")): - return self.version[0] - elif type(self.version) is items.InlineTable: - if self.version.get("version", "").startswith(("^", "~")): - return self.version["version"][0] - return "" # dependencies with exact version or multiple versions +from poetryup.models.dependency import Dependency class Pyproject: @@ -47,14 +23,15 @@ class Pyproject: def __init__(self, pyproject_str: str) -> None: self.pyproject = tomlkit.loads(pyproject_str) self.poetry_version = version_.parse(self.__get_poetry_version()) + self._dependencies = None # caches the dependencies - def dumps(self) -> str: - """Dumps pyproject into a string.""" - - return tomlkit.dumps(self.pyproject) + @property + def dependencies(self) -> List[Dependency]: + """The pyproject dependencies""" - def list_dependencies(self) -> List[Dependency]: - """Returns pyproject dependencies""" + if self._dependencies is not None: + # return cached dependencies + return self._dependencies dependencies: List[Dependency] = [] table = self.pyproject["tool"]["poetry"] @@ -90,53 +67,105 @@ def list_dependencies(self) -> List[Dependency]: ) dependencies.append(dependency) + self._dependencies = dependencies # cache dependencies return dependencies - def list_lock_dependencies(self) -> List[Dependency]: - """Returns pyproject dependencies with their lock version""" + @property + def lock_dependencies(self) -> List[Dependency]: + """The pyproject dependencies with their lock version""" - # create list of lock dependencies + # run poetry show to get currently installed dependencies output = self.__run_poetry_show() + + # create dependencies from each line of the output pattern = re.compile("^[a-zA-Z-]+") - lock_deps: List[Dependency] = [] + lock_dependencies: List[Dependency] = [] for line in output.split("\n"): - if pattern.match(line) is not None: - name, version, *_ = line.split() - dependency = Dependency( - name=name, - version=version, - group="", + if pattern.match(line) is None: + # not a matching line, continue to next + continue + + # extract name and version + lock_name, lock_version, *_ = line.split() + + # search for dependency in pyproject + dependency = self.search_dependency(self.dependencies, lock_name) + if dependency is None: + # dependency not found, continue to next + continue + + lock_dependencies.append( + Dependency( + name=dependency.name, + version=lock_version, + group=dependency.group, ) - lock_deps.append(dependency) + ) - # list dependencies from pyproject and set version to lock version - dependencies = self.list_dependencies() - for dependency in dependencies: - lock_dep = next( - ( - lock_dep - for lock_dep in lock_deps - if lock_dep.normalized_name == dependency.normalized_name - ), - None, + return lock_dependencies + + @property + def bumped_dependencies(self) -> List[Dependency]: + """The pyproject dependencies with their version bumped to lock version + + Lock versions will be used if applicable. For instance, using the lock + version for a dependency that is specified with the inequality + constraint '!=x.y.z' would completely change its meaning. + """ + + lock_dependencies = self.lock_dependencies + + bumped_dependencies: List[Dependency] = [] + for dependency in self.dependencies: + # search for lock dependency + lock_dependency = self.search_dependency( + lock_dependencies, + dependency.name, ) - if lock_dep is None: - logging.info( - f"Couldn't find lock dependency for '{dependency.name}'" - ) - continue - if type(dependency.version) is items.String: - dependency.version = dependency.constraint + lock_dep.version + version = dependency.version + if isinstance(version, str): + version = dependency.constraint + lock_dependency.version elif ( - type(dependency.version) is items.InlineTable - and dependency.version.get("version") is not None + isinstance(version, Dict) and version.get("version") is not None ): - dependency.version["version"] = ( - dependency.constraint + lock_dep.version + version["version"] = ( + dependency.constraint + lock_dependency.version ) - return dependencies + bumped_dependencies.append( + Dependency( + name=dependency.name, + version=version, + group=dependency.group, + ) + ) + + return bumped_dependencies + + def dumps(self) -> str: + """Dumps pyproject into a string.""" + + return tomlkit.dumps(self.pyproject) + + def search_dependency( + self, + dependencies: List[Dependency], + name: str, + ) -> Union[Dependency, None]: + """Search for a dependency by name given a list of dependencies + + Args: + dependencies: A list of dependencies to search in + name: Name of the dependency to search for + + Returns: + A dependency if found, None if not found + """ + + for dependency in dependencies: + if dependency.name == name or dependency.normalized_name == name: + return dependency def update_dependencies( self, @@ -156,11 +185,11 @@ def update_dependencies( # to avoid version solver error in case dependencies depend on each # other groups = {} - for dependency in self.list_dependencies(): + for dependency in self.dependencies: if skip_exact and dependency.constraint == "": # skip dependencies with an exact version continue - if type(dependency.version) is items.String: + if isinstance(dependency.version, str): groups[dependency.group] = groups.get( dependency.group, [] ) + [f"{dependency.name}@latest"] @@ -176,7 +205,7 @@ def update_dependencies( # bump versions in pyproject table = self.pyproject["tool"]["poetry"] - for dependency in self.list_lock_dependencies(): + for dependency in self.bumped_dependencies: if dependency.group == "default": table["dependencies"][dependency.name] = dependency.version elif ( diff --git a/src/poetryup/main.py b/src/poetryup/main.py index 8cb9e92..7c41d38 100644 --- a/src/poetryup/main.py +++ b/src/poetryup/main.py @@ -6,7 +6,7 @@ import typer -from poetryup.pyproject import Pyproject +from poetryup.core.pyproject import Pyproject logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO").upper()) diff --git a/src/poetryup/models/dependency.py b/src/poetryup/models/dependency.py new file mode 100644 index 0000000..6ce8765 --- /dev/null +++ b/src/poetryup/models/dependency.py @@ -0,0 +1,32 @@ +from dataclasses import dataclass +from typing import Dict, List, Union + + +@dataclass(frozen=True) +class Dependency: + """A class to represent a dependency + + Args: + name: The name of the dependency + version: The version of the dependency + group: The group of the dependency + """ + + name: str + version: Union[str, Dict, List] + group: str + + @property + def normalized_name(self) -> str: + # https://www.python.org/dev/peps/pep-0503/#normalized-names + return self.name.replace("_", "-").lower() + + @property + def constraint(self) -> str: + if isinstance(self.version, str): + if self.version[0].startswith(("^", "~")): + return self.version[0] + elif isinstance(self.version, Dict): + if self.version.get("version", "").startswith(("^", "~")): + return self.version["version"][0] + return "" # dependencies with exact version or multiple versions diff --git a/tests/conftest.py b/tests/conftest.py index f1093fb..0263368 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ import pytest from pytest_mock import MockerFixture -from poetryup.pyproject import Pyproject +from poetryup.core.pyproject import Pyproject @pytest.fixture(scope="function") @@ -18,10 +18,28 @@ def mock_poetry_commands(mocker: MockerFixture) -> None: Pyproject, "_Pyproject__run_poetry_show", return_value=( - "poetryup 0.2.0 Update dependencies and bump their version in the " + "poetryup 0.2.0 " "pyproject.toml file" "\n└── toml >=0.10.2,<0.11.0\n" - "poetryup-extra 0.2.0 " + "poetryup-caret 0.2.0 " + "pyproject.toml file" + "\n└── toml >=0.10.2,<0.11.0\n" + "poetryup-tilde 0.2.0 " + "pyproject.toml file" + "\n└── toml >=0.10.2,<0.11.0\n" + "poetryup-exact 0.2.0 " + "pyproject.toml file" + "\n└── toml >=0.10.2,<0.11.0\n" + "poetryup-restricted 0.2.0 " + "pyproject.toml file" + "\n└── toml >=0.10.2,<0.11.0\n" + "poetryup-git 0.2.0 " + "pyproject.toml file" + "\n└── toml >=0.10.2,<0.11.0\n" + "poetryup-underscore 0.2.0 " + "pyproject.toml file" + "\n└── toml >=0.10.2,<0.11.0\n" + "poetryup-capital 0.2.0 " "pyproject.toml file" "\n└── toml >=0.10.2,<0.11.0\n" ), diff --git a/tests/unit/fixtures/expected_pyproject/pyproject.toml b/tests/unit/fixtures/expected_pyproject/pyproject.toml index b6bab57..877ce1c 100644 --- a/tests/unit/fixtures/expected_pyproject/pyproject.toml +++ b/tests/unit/fixtures/expected_pyproject/pyproject.toml @@ -22,7 +22,16 @@ include = ["LICENSE"] python = "^3.6" poetryup = "^0.2.0" -[tool.poetry.dev-dependencies] +[tool.poetry.group.main.dependencies] +poetryup_caret = "^0.2.0" +poetryup_tilde = "~0.2.0" +poetryup_exact = "0.2.0" +poetryup_restricted = { version = "^0.2.0", python = "<3.7" } +poetryup_git = { git = "https://github.com/MousaZeidBaker/poetryup.git" } +poetryup_underscore = "^0.2.0" +Poetryup_Capital = "^0.2.0" + +[tool.poetry.group.dev.dependencies] [tool.poetry.scripts] poetryup = "src.test_poetryup.test_poetryup:main" diff --git a/tests/unit/fixtures/expected_pyproject/pyproject_with_capital_letters.toml b/tests/unit/fixtures/expected_pyproject/pyproject_with_capital_letters.toml deleted file mode 100644 index e08dd64..0000000 --- a/tests/unit/fixtures/expected_pyproject/pyproject_with_capital_letters.toml +++ /dev/null @@ -1,32 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" -PoetryUp = "^0.2.0" - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/fixtures/expected_pyproject/pyproject_with_dependency_groups.toml b/tests/unit/fixtures/expected_pyproject/pyproject_with_dependency_groups.toml deleted file mode 100644 index 137f826..0000000 --- a/tests/unit/fixtures/expected_pyproject/pyproject_with_dependency_groups.toml +++ /dev/null @@ -1,34 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" - -[tool.poetry.group.main.dependencies] -poetryup = "^0.2.0" - -[tool.poetry.group.dev.dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/fixtures/expected_pyproject/pyproject_with_exact_version_dependency.toml b/tests/unit/fixtures/expected_pyproject/pyproject_with_exact_version_dependency.toml deleted file mode 100644 index d753455..0000000 --- a/tests/unit/fixtures/expected_pyproject/pyproject_with_exact_version_dependency.toml +++ /dev/null @@ -1,32 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" -poetryup = "0.2.0" - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/fixtures/expected_pyproject/pyproject_with_git_dependency.toml b/tests/unit/fixtures/expected_pyproject/pyproject_with_git_dependency.toml deleted file mode 100644 index d67dbd3..0000000 --- a/tests/unit/fixtures/expected_pyproject/pyproject_with_git_dependency.toml +++ /dev/null @@ -1,32 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" -poetryup = { git = "https://github.com/MousaZeidBaker/poetryup.git" } - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/fixtures/expected_pyproject/pyproject_with_restricted_dependency.toml b/tests/unit/fixtures/expected_pyproject/pyproject_with_restricted_dependency.toml deleted file mode 100644 index 2d31915..0000000 --- a/tests/unit/fixtures/expected_pyproject/pyproject_with_restricted_dependency.toml +++ /dev/null @@ -1,32 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" -poetryup = {version = "^0.2.0", python = "<3.7"} - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/fixtures/expected_pyproject/pyproject_with_underscore_character.toml b/tests/unit/fixtures/expected_pyproject/pyproject_with_underscore_character.toml deleted file mode 100644 index c87a522..0000000 --- a/tests/unit/fixtures/expected_pyproject/pyproject_with_underscore_character.toml +++ /dev/null @@ -1,32 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" -poetryup_extra = "^0.2.0" - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/fixtures/input_pyproject/pyproject.toml b/tests/unit/fixtures/input_pyproject/pyproject.toml index f57b44c..e34378e 100644 --- a/tests/unit/fixtures/input_pyproject/pyproject.toml +++ b/tests/unit/fixtures/input_pyproject/pyproject.toml @@ -22,7 +22,16 @@ include = ["LICENSE"] python = "^3.6" poetryup = "^0.1.0" -[tool.poetry.dev-dependencies] +[tool.poetry.group.main.dependencies] +poetryup_caret = "^0.1.0" +poetryup_tilde = "~0.1.0" +poetryup_exact = "0.1.0" +poetryup_restricted = { version = "^0.1.0", python = "<3.7" } +poetryup_git = { git = "https://github.com/MousaZeidBaker/poetryup.git" } +poetryup_underscore = "^0.1.0" +Poetryup_Capital = "^0.1.0" + +[tool.poetry.group.dev.dependencies] [tool.poetry.scripts] poetryup = "src.test_poetryup.test_poetryup:main" diff --git a/tests/unit/fixtures/input_pyproject/pyproject_with_capital_letters.toml b/tests/unit/fixtures/input_pyproject/pyproject_with_capital_letters.toml deleted file mode 100644 index dea99fe..0000000 --- a/tests/unit/fixtures/input_pyproject/pyproject_with_capital_letters.toml +++ /dev/null @@ -1,32 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" -PoetryUp = "^0.1.0" - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/fixtures/input_pyproject/pyproject_with_dependency_groups.toml b/tests/unit/fixtures/input_pyproject/pyproject_with_dependency_groups.toml deleted file mode 100644 index 9b1cc17..0000000 --- a/tests/unit/fixtures/input_pyproject/pyproject_with_dependency_groups.toml +++ /dev/null @@ -1,34 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" - -[tool.poetry.group.main.dependencies] -poetryup = "^0.1.0" - -[tool.poetry.group.dev.dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/fixtures/input_pyproject/pyproject_with_exact_version_dependency.toml b/tests/unit/fixtures/input_pyproject/pyproject_with_exact_version_dependency.toml deleted file mode 100644 index fbcfe8e..0000000 --- a/tests/unit/fixtures/input_pyproject/pyproject_with_exact_version_dependency.toml +++ /dev/null @@ -1,32 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" -poetryup = "0.1.0" - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/fixtures/input_pyproject/pyproject_with_git_dependency.toml b/tests/unit/fixtures/input_pyproject/pyproject_with_git_dependency.toml deleted file mode 100644 index d67dbd3..0000000 --- a/tests/unit/fixtures/input_pyproject/pyproject_with_git_dependency.toml +++ /dev/null @@ -1,32 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" -poetryup = { git = "https://github.com/MousaZeidBaker/poetryup.git" } - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/fixtures/input_pyproject/pyproject_with_restricted_dependency.toml b/tests/unit/fixtures/input_pyproject/pyproject_with_restricted_dependency.toml deleted file mode 100644 index c252bec..0000000 --- a/tests/unit/fixtures/input_pyproject/pyproject_with_restricted_dependency.toml +++ /dev/null @@ -1,32 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" -poetryup = {version = "^0.1.0", python = "<3.7"} - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/fixtures/input_pyproject/pyproject_with_underscore_character.toml b/tests/unit/fixtures/input_pyproject/pyproject_with_underscore_character.toml deleted file mode 100644 index 017215e..0000000 --- a/tests/unit/fixtures/input_pyproject/pyproject_with_underscore_character.toml +++ /dev/null @@ -1,32 +0,0 @@ -[tool.poetry] -name = "test-poetryup" -version = "0.1.0" -description = "Test PoetryUp" -authors = ["Mousa Zeid Baker"] -packages = [ - { include = "test_poetryup", from = "src" } -] -license = "MIT" -readme = "README.md" -homepage = "https://github.com/MousaZeidBaker/poetryup" -repository = "https://github.com/MousaZeidBaker/poetryup" -keywords=[ - "packaging", - "dependency", - "poetry", - "poetryup", -] -include = ["LICENSE"] - -[tool.poetry.dependencies] -python = "^3.6" -poetryup_extra = "^0.1.0" - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -poetryup = "src.test_poetryup.test_poetryup:main" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/tests/unit/test_dependency.py b/tests/unit/test_dependency.py new file mode 100644 index 0000000..8e3f976 --- /dev/null +++ b/tests/unit/test_dependency.py @@ -0,0 +1,33 @@ +from poetryup.models.dependency import Dependency + + +def test_normalized_name() -> None: + dependency = Dependency( + name="poetry_up", + version="0.1.0", + group="default", + ) + assert dependency.normalized_name == "poetry-up" + + +def test_constraint() -> None: + dependency = Dependency( + name="poetry_up", + version="^0.1.0", + group="default", + ) + assert dependency.constraint == "^" + + dependency = Dependency( + name="poetry_up", + version={"version": "^0.1.0"}, + group="default", + ) + assert dependency.constraint == "^" + + dependency = Dependency( + name="poetry_up", + version=[], + group="default", + ) + assert dependency.constraint == "" diff --git a/tests/unit/test_pyproject.py b/tests/unit/test_pyproject.py index fee083e..e45faf7 100644 --- a/tests/unit/test_pyproject.py +++ b/tests/unit/test_pyproject.py @@ -1,405 +1,128 @@ import os from pathlib import Path +from unittest.mock import call from pytest_mock import MockerFixture -from poetryup.pyproject import Pyproject +from poetryup.core.pyproject import Pyproject - -def test_update_dependencies( - mock_poetry_commands, -) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/pyproject.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/pyproject.toml", - ) - ).read_text() - - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies() - assert pyproject.dumps() == expected_pyproject_str - assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["poetryup"] - == "^0.2.0" +pyproject_str = Path( + os.path.join( + os.path.dirname(__file__), + "fixtures/input_pyproject/pyproject.toml", ) +).read_text() - -def test_update_dependencies_latest( - mock_poetry_commands, -) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/pyproject.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/pyproject.toml", - ) - ).read_text() - - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies(latest=True) - assert pyproject.dumps() == expected_pyproject_str - assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["poetryup"] - == "^0.2.0" +expected_pyproject_str = Path( + os.path.join( + os.path.dirname(__file__), + "fixtures/expected_pyproject/pyproject.toml", ) +).read_text() -def test_update_dependencies_with_capital_letters( +def test_update_dependencies( mock_poetry_commands, ) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/pyproject_with_capital_letters.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/pyproject_with_capital_letters.toml", - ) - ).read_text() - pyproject = Pyproject(pyproject_str) pyproject.update_dependencies() - assert pyproject.dumps() == expected_pyproject_str - assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["PoetryUp"] - == "^0.2.0" - ) + table = pyproject.pyproject["tool"]["poetry"]["dependencies"] + assert table["poetryup"] == "^0.2.0" + + table = pyproject.pyproject["tool"]["poetry"]["group"]["main"][ + "dependencies" + ] + assert table["poetryup_caret"] == "^0.2.0" + assert table["poetryup_tilde"] == "~0.2.0" + assert table["poetryup_exact"] == "0.2.0" + assert table["poetryup_restricted"] == { + "version": "^0.2.0", + "python": "<3.7", + } + assert table["poetryup_git"] == { + "git": "https://github.com/MousaZeidBaker/poetryup.git" + } + assert table["poetryup_underscore"] == "^0.2.0" + assert table["Poetryup_Capital"] == "^0.2.0" -def test_update_dependencies_latest_with_capital_letters( - mock_poetry_commands, -) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/pyproject_with_capital_letters.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/pyproject_with_capital_letters.toml", - ) - ).read_text() - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies(latest=True) - assert pyproject.dumps() == expected_pyproject_str - assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["PoetryUp"] - == "^0.2.0" - ) - - -def test_update_dependencies_with_underscore_character( +def test_update_dependencies_latest( mock_poetry_commands, + mocker: MockerFixture, ) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/pyproject_with_underscore_character.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/", - "pyproject_with_underscore_character.toml", - ) - ).read_text() - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies() - assert pyproject.dumps() == expected_pyproject_str - assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["poetryup_extra"] - == "^0.2.0" + mock = mocker.patch.object( + pyproject, + "_Pyproject__run_poetry_add", + return_value=None, ) - - -def test_update_dependencies_latest_with_underscore_character( - mock_poetry_commands, -) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/", - "pyproject_with_underscore_character.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/", - "pyproject_with_underscore_character.toml", - ) - ).read_text() - - pyproject = Pyproject(pyproject_str) pyproject.update_dependencies(latest=True) - assert pyproject.dumps() == expected_pyproject_str - assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["poetryup_extra"] - == "^0.2.0" - ) - -def test_update_dependencies_with_dependency_groups( - mock_poetry_commands, -) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/pyproject_with_dependency_groups.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/pyproject_with_dependency_groups.toml", - ) - ).read_text() - - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies() - assert pyproject.dumps() == expected_pyproject_str - assert ( - pyproject.pyproject["tool"]["poetry"]["group"]["main"]["dependencies"][ - "poetryup" - ] - == "^0.2.0" - ) + calls = [ + call( + packages=["poetryup@latest"], + group="default", + ), + call( + packages=[ + "poetryup_caret@latest", + "poetryup_tilde@latest", + "poetryup_exact@latest", + "poetryup_underscore@latest", + "Poetryup_Capital@latest", + ], + group="main", + ), + ] + mock.assert_has_calls(calls) -def test_update_dependencies_latest_with_dependency_groups( +def test_search_dependency( mock_poetry_commands, ) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/pyproject_with_dependency_groups.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/pyproject_with_dependency_groups.toml", - ) - ).read_text() - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies(latest=True) - assert pyproject.dumps() == expected_pyproject_str + name = pyproject.dependencies[0].name assert ( - pyproject.pyproject["tool"]["poetry"]["group"]["main"]["dependencies"][ - "poetryup" - ] - == "^0.2.0" - ) - - -def test_update_dependencies_with_exact_version_dependency( - mock_poetry_commands, -) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/", - "pyproject_with_exact_version_dependency.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/", - "pyproject_with_exact_version_dependency.toml", + pyproject.search_dependency( + pyproject.dependencies, + name, ) - ).read_text() - - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies() - assert pyproject.dumps() == expected_pyproject_str - assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["poetryup"] - == "0.2.0" + is not None ) -def test_update_dependencies_latest_with_exact_version_dependency( +def test_search_dependency_by_normalized_name( mock_poetry_commands, ) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/", - "pyproject_with_exact_version_dependency.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/", - "pyproject_with_exact_version_dependency.toml", - ) - ).read_text() - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies(latest=True) - assert pyproject.dumps() == expected_pyproject_str + normalized_name = pyproject.dependencies[0].normalized_name assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["poetryup"] - == "0.2.0" - ) - - -def test_update_dependencies_latest_skip_exact_with_exact_version_dependency( - mock_poetry_commands, - mocker: MockerFixture, -) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/", - "pyproject_with_exact_version_dependency.toml", + pyproject.search_dependency( + pyproject.dependencies, + normalized_name, ) - ).read_text() - - mocker.patch.object( - Pyproject, - "_Pyproject__run_poetry_show", - return_value=( - "poetryup 0.1.0 Update dependencies and bump their version in the " - "pyproject.toml file" - "\n└── toml >=0.10.2,<0.11.0\n" - ), - ) - - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies(latest=True) - assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["poetryup"] - == "0.1.0" + is not None ) -def test_update_dependencies_with_git_dependency( +def test_search_dependency_non_existent( mock_poetry_commands, ) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/pyproject_with_git_dependency.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/pyproject_with_git_dependency.toml", - ) - ).read_text() - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies() - assert pyproject.dumps() == expected_pyproject_str assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["poetryup"]["git"] - == "https://github.com/MousaZeidBaker/poetryup.git" - ) - - -def test_update_dependencies_latest_with_git_dependency( - mock_poetry_commands, -) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/pyproject_with_git_dependency.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/pyproject_with_git_dependency.toml", + pyproject.search_dependency( + pyproject.dependencies, + "non_existent", ) - ).read_text() - - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies(latest=True) - assert pyproject.dumps() == expected_pyproject_str - assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["poetryup"]["git"] - == "https://github.com/MousaZeidBaker/poetryup.git" + is None ) -def test_update_dependencies_with_restricted_dependency( +def test_dumps( mock_poetry_commands, ) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/", - "pyproject_with_restricted_dependency.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/", - "pyproject_with_restricted_dependency.toml", - ) - ).read_text() - pyproject = Pyproject(pyproject_str) pyproject.update_dependencies() - assert pyproject.dumps() == expected_pyproject_str - assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["poetryup"][ - "version" - ] - == "^0.2.0" - ) - -def test_update_dependencies_latest_with_restricted_dependency( - mock_poetry_commands, -) -> None: - pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/input_pyproject/", - "pyproject_with_restricted_dependency.toml", - ) - ).read_text() - expected_pyproject_str = Path( - os.path.join( - os.path.dirname(__file__), - "fixtures/expected_pyproject/", - "pyproject_with_restricted_dependency.toml", - ) - ).read_text() - - pyproject = Pyproject(pyproject_str) - pyproject.update_dependencies(latest=True) assert pyproject.dumps() == expected_pyproject_str - assert ( - pyproject.pyproject["tool"]["poetry"]["dependencies"]["poetryup"][ - "version" - ] - == "^0.2.0" - )