Skip to content

Commit

Permalink
Warn major version difference
Browse files Browse the repository at this point in the history
Targeting #348, from now on, if we cannot check the copier version or the installed version's major part is bigger than the template's minimal major part, there'll be proper warnings.
  • Loading branch information
yajo committed Feb 14, 2021
1 parent b3190c2 commit 10e87b7
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 18 deletions.
34 changes: 26 additions & 8 deletions copier/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,24 @@
from .types import PathSeq


class UserMessageError(Exception):
# Errors
class CopierError(Exception):
"""Base class for all other Copier errors."""


class UserMessageError(CopierError):
"""Exit the program giving a message to the user."""


class UnsupportedVersionError(UserMessageError):
class UnsupportedVersionError(UserMessageError, CopierError):
"""Copier version does not support template version."""


class ConfigFileError(ValueError):
class ConfigFileError(ValueError, CopierError):
"""Parent class defining problems with the config file."""


class InvalidConfigFileError(ConfigFileError):
class InvalidConfigFileError(ConfigFileError, CopierError):
"""Indicates that the config file is wrong."""

def __init__(self, conf_path: Path, quiet: bool):
Expand All @@ -29,7 +34,7 @@ def __init__(self, conf_path: Path, quiet: bool):
super().__init__(msg)


class MultipleConfigFilesError(ConfigFileError):
class MultipleConfigFilesError(ConfigFileError, CopierError):
"""Both copier.yml and copier.yaml found, and that's an error."""

def __init__(self, conf_paths: "PathSeq", quiet: bool):
Expand All @@ -38,19 +43,32 @@ def __init__(self, conf_paths: "PathSeq", quiet: bool):
super().__init__(msg)


class InvalidTypeError(TypeError):
class InvalidTypeError(TypeError, CopierError):
"""The question type is not among the supported ones."""


class PathNotAbsoluteError(_PathValueError):
class PathNotAbsoluteError(_PathValueError, CopierError):
"""The path is not absolute, but it should be."""

code = "path.not_absolute"
msg_template = '"{path}" is not an absolute path'


class PathNotRelativeError(_PathValueError):
class PathNotRelativeError(_PathValueError, CopierError):
"""The path is not relative, but it should be."""

code = "path.not_relative"
msg_template = '"{path}" is not a relative path'


# Warnings
class CopierWarning(Warning):
"""Base class for all other Copier warnings."""


class UnknownCopierVersionWarning(UserWarning, CopierWarning):
"""Cannot determine installed Copier version."""


class OldTemplateWarning(UserWarning, CopierWarning):
"""Template was designed for an older Copier version."""
36 changes: 28 additions & 8 deletions copier/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@
from contextlib import suppress
from pathlib import Path
from typing import List, Mapping, Optional, Sequence, Set, Tuple
from warnings import warn

from packaging import version
from packaging.version import parse
from packaging.version import Version, parse
from plumbum.cmd import git
from plumbum.machines import local
from pydantic.dataclasses import dataclass

from .errors import UnsupportedVersionError
from .errors import (
OldTemplateWarning,
UnknownCopierVersionWarning,
UnsupportedVersionError,
)
from .types import AnyByStrDict, OptStr, StrSeq, VCSTypes
from .user_data import load_config_data
from .vcs import checkout_latest_tag, clone, get_repo
Expand Down Expand Up @@ -53,22 +57,38 @@ def filter_config(data: AnyByStrDict) -> Tuple[AnyByStrDict, AnyByStrDict]:
return conf_data, questions_data


def verify_minimum_version(version_str: str) -> None:
"""Raise an error if the current Copier version is less than the given version."""
def verify_copier_version(version_str: str) -> None:
"""Raise an error if the current Copier version is less than the given version.
Args:
version_str:
Minimal copier version for the template.
"""
# Importing __version__ at the top of the module creates a circular import
# ("cannot import name '__version__' from partially initialized module 'copier'"),
# so instead we do a lazy import here
from . import __version__

# Disable check when running copier as editable installation
if __version__ == "0.0.0":
warn(
"Cannot check Copier version constraint.",
UnknownCopierVersionWarning,
)
return

if version.parse(__version__) < version.parse(version_str):
parsed_installed, parsed_min = map(Version, (__version__, version_str))
if parsed_installed < parsed_min:
raise UnsupportedVersionError(
f"This template requires Copier version >= {version_str}, "
f"while your version of Copier is {__version__}."
)
if parsed_installed.major > parsed_min.major:
warn(
f"This template was designed for Copier {version_str}, "
f"but your version of Copier is {__version__}. "
f"You could find some incompatibilities.",
OldTemplateWarning,
)


@dataclass
Expand Down Expand Up @@ -120,7 +140,7 @@ def _raw_config(self) -> AnyByStrDict:
"""
result = load_config_data(self.local_abspath)
with suppress(KeyError):
verify_minimum_version(result["_min_copier_version"])
verify_copier_version(result["_min_copier_version"])
return result

@cached_property
Expand Down
21 changes: 19 additions & 2 deletions tests/test_minimum_version.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import warnings

import pytest
from plumbum import local
from plumbum.cmd import git

import copier
from copier.errors import UnsupportedVersionError
from copier.errors import (
OldTemplateWarning,
UnknownCopierVersionWarning,
UnsupportedVersionError,
)

from .helpers import build_file_tree

Expand Down Expand Up @@ -68,4 +74,15 @@ def test_minimum_version_update(template_path, tmp_path, monkeypatch):
def test_version_0_0_0_ignored(template_path, tmp_path, monkeypatch):
monkeypatch.setattr("copier.__version__", "0.0.0")
# assert no error
copier.copy(template_path, tmp_path)
with warnings.catch_warnings():
warnings.simplefilter("error")
with pytest.raises(UnknownCopierVersionWarning):
copier.run_copy(template_path, tmp_path)


def test_version_bigger_major_warning(template_path, tmp_path, monkeypatch):
monkeypatch.setattr("copier.__version__", "11.0.0a0")
with warnings.catch_warnings():
warnings.simplefilter("error")
with pytest.raises(OldTemplateWarning):
copier.run_copy(template_path, tmp_path)

0 comments on commit 10e87b7

Please sign in to comment.