mirror of https://github.com/explosion/spaCy.git
Use minor version for compatibility check (#8403)
* Use minor version for compatibility check * Use minor version of compatibility table * Soften warning message about incompatible models * Add test for presence of current version in compatibility table * Add test for download compatibility table * Use minor version of lower pin in error message if possible * Fall back to spacy_git_version if available * Fix unknown version string
This commit is contained in:
parent
ec71a6b572
commit
9fde258053
|
@ -6,7 +6,7 @@ import typer
|
|||
|
||||
from ._util import app, Arg, Opt, WHEEL_SUFFIX, SDIST_SUFFIX
|
||||
from .. import about
|
||||
from ..util import is_package, get_base_version, run_command
|
||||
from ..util import is_package, get_minor_version, run_command
|
||||
from ..errors import OLD_MODEL_SHORTCUTS
|
||||
|
||||
|
||||
|
@ -74,7 +74,7 @@ def download(model: str, direct: bool = False, sdist: bool = False, *pip_args) -
|
|||
|
||||
|
||||
def get_compatibility() -> dict:
|
||||
version = get_base_version(about.__version__)
|
||||
version = get_minor_version(about.__version__)
|
||||
r = requests.get(about.__compatibility__)
|
||||
if r.status_code != 200:
|
||||
msg.fail(
|
||||
|
|
|
@ -3,10 +3,12 @@ from pathlib import Path
|
|||
import sys
|
||||
import requests
|
||||
from wasabi import msg, Printer
|
||||
import warnings
|
||||
|
||||
from ..errors import Warnings
|
||||
from ._util import app
|
||||
from .. import about
|
||||
from ..util import get_package_version, get_installed_models, get_base_version
|
||||
from ..util import get_package_version, get_installed_models, get_minor_version
|
||||
from ..util import get_package_path, get_model_meta, is_compatible_version
|
||||
|
||||
|
||||
|
@ -24,7 +26,7 @@ def validate_cli():
|
|||
|
||||
def validate() -> None:
|
||||
model_pkgs, compat = get_model_pkgs()
|
||||
spacy_version = get_base_version(about.__version__)
|
||||
spacy_version = get_minor_version(about.__version__)
|
||||
current_compat = compat.get(spacy_version, {})
|
||||
if not current_compat:
|
||||
msg.warn(f"No compatible packages found for v{spacy_version} of spaCy")
|
||||
|
@ -44,8 +46,8 @@ def validate() -> None:
|
|||
comp = msg.text("", color="green", icon="good", no_print=True)
|
||||
version = msg.text(data["version"], color="green", no_print=True)
|
||||
else:
|
||||
version = msg.text(data["version"], color="red", no_print=True)
|
||||
comp = f"--> {compat.get(data['name'], ['n/a'])[0]}"
|
||||
version = msg.text(data["version"], color="yellow", no_print=True)
|
||||
comp = f"--> {current_compat.get(data['name'], ['n/a'])[0]}"
|
||||
rows.append((data["name"], data["spacy"], version, comp))
|
||||
msg.table(rows, header=header)
|
||||
else:
|
||||
|
@ -78,6 +80,8 @@ def get_model_pkgs(silent: bool = False) -> Tuple[dict, dict]:
|
|||
msg.good("Loaded compatibility table")
|
||||
compat = r.json()["spacy"]
|
||||
all_models = set()
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings("ignore", message="\\[W09[45]")
|
||||
installed_models = get_installed_models()
|
||||
for spacy_v, models in dict(compat).items():
|
||||
all_models.update(models.keys())
|
||||
|
@ -92,6 +96,8 @@ def get_model_pkgs(silent: bool = False) -> Tuple[dict, dict]:
|
|||
spacy_version = about.__version__
|
||||
else:
|
||||
model_path = get_package_path(package)
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings("ignore", message="\\[W09[45]")
|
||||
model_meta = get_model_meta(model_path)
|
||||
spacy_version = model_meta.get("spacy_version", "n/a")
|
||||
is_compat = is_compatible_version(about.__version__, spacy_version)
|
||||
|
|
|
@ -150,12 +150,12 @@ class Warnings:
|
|||
"released, because the model may say it's compatible when it's "
|
||||
'not. Consider changing the "spacy_version" in your meta.json to a '
|
||||
"version range, with a lower and upper pin. For example: {example}")
|
||||
W095 = ("Model '{model}' ({model_version}) requires spaCy {version} and is "
|
||||
"incompatible with the current version ({current}). This may lead "
|
||||
"to unexpected results or runtime errors. To resolve this, "
|
||||
"download a newer compatible model or retrain your custom model "
|
||||
"with the current spaCy version. For more details and available "
|
||||
"updates, run: python -m spacy validate")
|
||||
W095 = ("Model '{model}' ({model_version}) was trained with spaCy "
|
||||
"{version} and may not be 100% compatible with the current version "
|
||||
"({current}). If you see errors or degraded performance, download "
|
||||
"a newer compatible model or retrain your custom model with the "
|
||||
"current spaCy version. For more details and available updates, "
|
||||
"run: python -m spacy validate")
|
||||
W096 = ("The method `nlp.disable_pipes` is now deprecated - use "
|
||||
"`nlp.select_pipes` instead.")
|
||||
W100 = ("Skipping unsupported morphological feature(s): '{feature}'. "
|
||||
|
|
|
@ -10,6 +10,10 @@ from spacy.cli.init_config import init_config, RECOMMENDATIONS
|
|||
from spacy.cli._util import validate_project_commands, parse_config_overrides
|
||||
from spacy.cli._util import load_project_config, substitute_project_variables
|
||||
from spacy.cli._util import string_to_list
|
||||
from spacy import about
|
||||
from spacy.util import get_minor_version
|
||||
from spacy.cli.validate import get_model_pkgs
|
||||
from spacy.cli.download import get_compatibility, get_version
|
||||
from thinc.api import ConfigValidationError, Config
|
||||
import srsly
|
||||
import os
|
||||
|
@ -308,7 +312,8 @@ def test_project_config_validation2(config, n_errors):
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"int_value", [10, pytest.param("10", marks=pytest.mark.xfail)],
|
||||
"int_value",
|
||||
[10, pytest.param("10", marks=pytest.mark.xfail)],
|
||||
)
|
||||
def test_project_config_interpolation(int_value):
|
||||
variables = {"a": int_value, "b": {"c": "foo", "d": True}}
|
||||
|
@ -331,7 +336,8 @@ def test_project_config_interpolation(int_value):
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"greeting", [342, "everyone", "tout le monde", pytest.param("42", marks=pytest.mark.xfail)],
|
||||
"greeting",
|
||||
[342, "everyone", "tout le monde", pytest.param("42", marks=pytest.mark.xfail)],
|
||||
)
|
||||
def test_project_config_interpolation_override(greeting):
|
||||
variables = {"a": "world"}
|
||||
|
@ -423,7 +429,13 @@ def test_parse_cli_overrides():
|
|||
@pytest.mark.parametrize("pretraining", [True, False])
|
||||
def test_init_config(lang, pipeline, optimize, pretraining):
|
||||
# TODO: add more tests and also check for GPU with transformers
|
||||
config = init_config(lang=lang, pipeline=pipeline, optimize=optimize, pretraining=pretraining, gpu=False)
|
||||
config = init_config(
|
||||
lang=lang,
|
||||
pipeline=pipeline,
|
||||
optimize=optimize,
|
||||
pretraining=pretraining,
|
||||
gpu=False,
|
||||
)
|
||||
assert isinstance(config, Config)
|
||||
if pretraining:
|
||||
config["paths"]["raw_text"] = "my_data.jsonl"
|
||||
|
@ -474,3 +486,18 @@ def test_string_to_list(value):
|
|||
def test_string_to_list_intify(value):
|
||||
assert string_to_list(value, intify=False) == ["1", "2", "3"]
|
||||
assert string_to_list(value, intify=True) == [1, 2, 3]
|
||||
|
||||
|
||||
def test_download_compatibility():
|
||||
model_name = "en_core_web_sm"
|
||||
compatibility = get_compatibility()
|
||||
version = get_version(model_name, compatibility)
|
||||
assert get_minor_version(about.__version__) == get_minor_version(version)
|
||||
|
||||
|
||||
def test_validate_compatibility_table():
|
||||
model_pkgs, compat = get_model_pkgs()
|
||||
spacy_version = get_minor_version(about.__version__)
|
||||
current_compat = compat.get(spacy_version, {})
|
||||
assert len(current_compat) > 0
|
||||
assert "en_core_web_sm" in current_compat
|
||||
|
|
|
@ -648,6 +648,19 @@ def get_model_version_range(spacy_version: str) -> str:
|
|||
return f">={spacy_version},<{release[0]}.{release[1] + 1}.0"
|
||||
|
||||
|
||||
def get_model_lower_version(constraint: str) -> Optional[str]:
|
||||
"""From a version range like >=1.2.3,<1.3.0 return the lower pin.
|
||||
"""
|
||||
try:
|
||||
specset = SpecifierSet(constraint)
|
||||
for spec in specset:
|
||||
if spec.operator in (">=", "==", "~="):
|
||||
return spec.version
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def get_base_version(version: str) -> str:
|
||||
"""Generate the base version without any prerelease identifiers.
|
||||
|
||||
|
@ -701,10 +714,18 @@ def load_meta(path: Union[str, Path]) -> Dict[str, Any]:
|
|||
raise ValueError(Errors.E054.format(setting=setting))
|
||||
if "spacy_version" in meta:
|
||||
if not is_compatible_version(about.__version__, meta["spacy_version"]):
|
||||
lower_version = get_model_lower_version(meta["spacy_version"])
|
||||
lower_version = get_minor_version(lower_version)
|
||||
if lower_version is not None:
|
||||
lower_version = "v" + lower_version
|
||||
elif "spacy_git_version" in meta:
|
||||
lower_version = "git commit " + meta["spacy_git_version"]
|
||||
else:
|
||||
lower_version = "version unknown"
|
||||
warn_msg = Warnings.W095.format(
|
||||
model=f"{meta['lang']}_{meta['name']}",
|
||||
model_version=meta["version"],
|
||||
version=meta["spacy_version"],
|
||||
version=lower_version,
|
||||
current=about.__version__,
|
||||
)
|
||||
warnings.warn(warn_msg)
|
||||
|
|
Loading…
Reference in New Issue