mirror of
https://github.com/copier-org/copier.git
synced 2025-05-05 15:32:54 +00:00
refactor: re-expose API with deprecation warnings on non-public API imports
This commit is contained in:
parent
ef5ea4b212
commit
2491c0681b
@ -4,10 +4,25 @@ Docs: https://copier.readthedocs.io/
|
||||
"""
|
||||
|
||||
import importlib.metadata
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from ._main import * # noqa: F401,F403
|
||||
from . import _main
|
||||
from ._deprecation import deprecate_member_as_internal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ._main import * # noqa: F403
|
||||
|
||||
try:
|
||||
__version__ = importlib.metadata.version(__name__)
|
||||
except importlib.metadata.PackageNotFoundError:
|
||||
__version__ = "0.0.0"
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if not name.startswith("_") and name not in {
|
||||
"run_copy",
|
||||
"run_recopy",
|
||||
"run_update",
|
||||
}:
|
||||
deprecate_member_as_internal(name, __name__)
|
||||
return getattr(_main, name)
|
||||
|
57
copier/_deprecation.py
Normal file
57
copier/_deprecation.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""Deprecation utilities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import warnings
|
||||
|
||||
_issue_note = (
|
||||
"If you have any questions or concerns, please raise an issue at "
|
||||
"<https://github.com/copier-org/copier/issues>."
|
||||
)
|
||||
|
||||
|
||||
def deprecate_module_as_internal(name: str) -> None:
|
||||
"""Deprecate a module as internal with a warning.
|
||||
|
||||
Args:
|
||||
name: The module name.
|
||||
"""
|
||||
warnings.warn(
|
||||
f"Importing from `{name}` is deprecated. This module is intended for internal "
|
||||
f"use only and will become inaccessible in the future. {_issue_note}",
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
|
||||
|
||||
def deprecate_member_as_internal(member: str, module: str) -> None:
|
||||
"""Deprecate a module member as internal with a warning.
|
||||
|
||||
Args:
|
||||
member: The module member name.
|
||||
module: The module name.
|
||||
"""
|
||||
warnings.warn(
|
||||
f"Importing `{member}` from `{module}` is deprecated. This module member is "
|
||||
"intended for internal use only and will become inaccessible in the future. "
|
||||
f"{_issue_note}",
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
|
||||
|
||||
def deprecate_member(member: str, module: str, new_import: str) -> None:
|
||||
"""Deprecate a module member with a new import statement with a warning.
|
||||
|
||||
Args:
|
||||
member: The module member name.
|
||||
module: The module name.
|
||||
new_import: The new import statement.
|
||||
"""
|
||||
warnings.warn(
|
||||
f"Importing `{member}` from `{module}` is deprecated. Please update the import "
|
||||
f"to `{new_import}`. The deprecated import will become invalid in the future. "
|
||||
f"{_issue_note}",
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
23
copier/cli.py
Normal file
23
copier/cli.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""Deprecated: module is intended for internal use only."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from copier import _cli
|
||||
from copier._deprecation import (
|
||||
deprecate_member_as_internal,
|
||||
deprecate_module_as_internal,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from copier._cli import * # noqa: F403
|
||||
|
||||
|
||||
deprecate_module_as_internal(__name__)
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if not name.startswith("_"):
|
||||
deprecate_member_as_internal(name, __name__)
|
||||
return getattr(_cli, name)
|
23
copier/jinja_ext.py
Normal file
23
copier/jinja_ext.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""Deprecated: module is intended for internal use only."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from copier import _jinja_ext
|
||||
from copier._deprecation import (
|
||||
deprecate_member_as_internal,
|
||||
deprecate_module_as_internal,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from copier._jinja_ext import * # noqa: F403
|
||||
|
||||
|
||||
deprecate_module_as_internal(__name__)
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if not name.startswith("_"):
|
||||
deprecate_member_as_internal(name, __name__)
|
||||
return getattr(_jinja_ext, name)
|
25
copier/main.py
Normal file
25
copier/main.py
Normal file
@ -0,0 +1,25 @@
|
||||
"""Deprecated: module is intended for internal use only."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from copier import _main
|
||||
from copier._deprecation import (
|
||||
deprecate_member,
|
||||
deprecate_member_as_internal,
|
||||
deprecate_module_as_internal,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from copier._main import * # noqa: F403
|
||||
|
||||
deprecate_module_as_internal(__name__)
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if name in {"run_copy", "run_recopy", "run_update"}:
|
||||
deprecate_member(name, __name__, f"from copier import {name}")
|
||||
elif not name.startswith("_"):
|
||||
deprecate_member_as_internal(name, __name__)
|
||||
return getattr(_main, name)
|
23
copier/subproject.py
Normal file
23
copier/subproject.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""Deprecated: module is intended for internal use only."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from copier import _subproject
|
||||
from copier._deprecation import (
|
||||
deprecate_member_as_internal,
|
||||
deprecate_module_as_internal,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from copier._subproject import * # noqa: F403
|
||||
|
||||
|
||||
deprecate_module_as_internal(__name__)
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if not name.startswith("_"):
|
||||
deprecate_member_as_internal(name, __name__)
|
||||
return getattr(_subproject, name)
|
23
copier/template.py
Normal file
23
copier/template.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""Deprecated: module is intended for internal use only."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from copier import _template
|
||||
from copier._deprecation import (
|
||||
deprecate_member_as_internal,
|
||||
deprecate_module_as_internal,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from copier._template import * # noqa: F403
|
||||
|
||||
|
||||
deprecate_module_as_internal(__name__)
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if not name.startswith("_"):
|
||||
deprecate_member_as_internal(name, __name__)
|
||||
return getattr(_template, name)
|
23
copier/tools.py
Normal file
23
copier/tools.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""Deprecated: module is intended for internal use only."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from copier import _tools
|
||||
from copier._deprecation import (
|
||||
deprecate_member_as_internal,
|
||||
deprecate_module_as_internal,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from copier._tools import * # noqa: F403
|
||||
|
||||
|
||||
deprecate_module_as_internal(__name__)
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if not name.startswith("_"):
|
||||
deprecate_member_as_internal(name, __name__)
|
||||
return getattr(_tools, name)
|
23
copier/types.py
Normal file
23
copier/types.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""Deprecated: module is intended for internal use only."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from copier import _types
|
||||
from copier._deprecation import (
|
||||
deprecate_member_as_internal,
|
||||
deprecate_module_as_internal,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from copier._types import * # noqa: F403
|
||||
|
||||
|
||||
deprecate_module_as_internal(__name__)
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if not name.startswith("_"):
|
||||
deprecate_member_as_internal(name, __name__)
|
||||
return getattr(_types, name)
|
23
copier/user_data.py
Normal file
23
copier/user_data.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""Deprecated: module is intended for internal use only."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from copier import _user_data
|
||||
from copier._deprecation import (
|
||||
deprecate_member_as_internal,
|
||||
deprecate_module_as_internal,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from copier._user_data import * # noqa: F403
|
||||
|
||||
|
||||
deprecate_module_as_internal(__name__)
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if not name.startswith("_"):
|
||||
deprecate_member_as_internal(name, __name__)
|
||||
return getattr(_user_data, name)
|
23
copier/vcs.py
Normal file
23
copier/vcs.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""Deprecated: module is intended for internal use only."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from copier import _vcs
|
||||
from copier._deprecation import (
|
||||
deprecate_member_as_internal,
|
||||
deprecate_module_as_internal,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from copier._vcs import * # noqa: F403
|
||||
|
||||
|
||||
deprecate_module_as_internal(__name__)
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if not name.startswith("_"):
|
||||
deprecate_member_as_internal(name, __name__)
|
||||
return getattr(_vcs, name)
|
@ -156,6 +156,20 @@ disable_error_code = ["no-untyped-def"]
|
||||
addopts = "-n auto -ra"
|
||||
markers = ["impure: needs network or is not 100% reproducible"]
|
||||
|
||||
[tool.coverage.run]
|
||||
omit = [
|
||||
# Ignore deprecated modules
|
||||
"copier/cli.py",
|
||||
"copier/jinja_ext.py",
|
||||
"copier/main.py",
|
||||
"copier/subproject.py",
|
||||
"copier/template.py",
|
||||
"copier/tools.py",
|
||||
"copier/types.py",
|
||||
"copier/user_data.py",
|
||||
"copier/vcs.py",
|
||||
]
|
||||
|
||||
[tool.commitizen]
|
||||
annotated_tag = true
|
||||
changelog_incremental = true
|
||||
|
@ -10,6 +10,7 @@ from plumbum import local
|
||||
from pydantic import ValidationError
|
||||
|
||||
import copier
|
||||
from copier._main import Worker
|
||||
from copier._template import DEFAULT_EXCLUDE, Task, Template, load_template_config
|
||||
from copier._types import AnyByStrDict
|
||||
from copier.errors import InvalidConfigFileError, MultipleConfigFilesError
|
||||
@ -322,12 +323,12 @@ def test_multiple_config_file_error() -> None:
|
||||
)
|
||||
def test_flags_bad_data(data: AnyByStrDict) -> None:
|
||||
with pytest.raises(ValidationError):
|
||||
copier.Worker(**data)
|
||||
Worker(**data)
|
||||
|
||||
|
||||
def test_flags_extra_fails() -> None:
|
||||
with pytest.raises(ValidationError):
|
||||
copier.Worker( # type: ignore[call-arg]
|
||||
Worker( # type: ignore[call-arg]
|
||||
src_path="..",
|
||||
dst_path=Path(),
|
||||
i_am_not_a_member="and_i_do_not_belong_here",
|
||||
@ -345,7 +346,7 @@ def is_subdict(small: dict[Any, Any], big: dict[Any, Any]) -> bool:
|
||||
|
||||
def test_worker_good_data(tmp_path: Path) -> None:
|
||||
# This test is probably useless, as it tests the what and not the how
|
||||
conf = copier.Worker("./tests/demo_data", tmp_path)
|
||||
conf = Worker("./tests/demo_data", tmp_path)
|
||||
assert conf._render_context()["_folder_name"] == tmp_path.name
|
||||
assert conf.all_exclusions == ("exclude1", "exclude2")
|
||||
assert conf.template.skip_if_exists == ["skip_if_exists1", "skip_if_exists2"]
|
||||
@ -373,12 +374,12 @@ def test_worker_good_data(tmp_path: Path) -> None:
|
||||
def test_worker_config_precedence(
|
||||
tmp_path: Path, test_input: AnyByStrDict, expected_exclusions: tuple[str, ...]
|
||||
) -> None:
|
||||
conf = copier.Worker(dst_path=tmp_path, vcs_ref="HEAD", **test_input)
|
||||
conf = Worker(dst_path=tmp_path, vcs_ref="HEAD", **test_input)
|
||||
assert expected_exclusions == conf.all_exclusions
|
||||
|
||||
|
||||
def test_config_data_transclusion() -> None:
|
||||
config = copier.Worker("tests/demo_transclude/demo")
|
||||
config = Worker("tests/demo_transclude/demo")
|
||||
assert config.all_exclusions == ("exclude1", "exclude2")
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@ from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from copier import Worker
|
||||
from copier._main import Worker
|
||||
from copier.errors import CopierAnswersInterrupt
|
||||
|
||||
from .helpers import build_file_tree
|
||||
|
@ -10,7 +10,7 @@ import yaml
|
||||
from pexpect.popen_spawn import PopenSpawn
|
||||
from plumbum import local
|
||||
|
||||
from copier import Worker
|
||||
from copier._main import Worker
|
||||
from copier._types import AnyByStrDict
|
||||
from copier._user_data import load_answersfile_data
|
||||
from copier.errors import InvalidTypeError
|
||||
|
Loading…
x
Reference in New Issue
Block a user