2017-10-12 21:05:06 +03:00
|
|
|
# coding: utf8
|
2017-10-27 15:38:39 +03:00
|
|
|
from __future__ import unicode_literals, print_function
|
2017-10-12 21:05:06 +03:00
|
|
|
|
|
|
|
from pathlib import Path
|
2018-01-03 23:20:35 +03:00
|
|
|
import sys
|
2018-05-20 21:26:56 +03:00
|
|
|
import requests
|
💫 Replace ujson, msgpack and dill/pickle/cloudpickle with srsly (#3003)
Remove hacks and wrappers, keep code in sync across our libraries and move spaCy a few steps closer to only depending on packages with binary wheels 🎉
See here: https://github.com/explosion/srsly
Serialization is hard, especially across Python versions and multiple platforms. After dealing with many subtle bugs over the years (encodings, locales, large files) our libraries like spaCy and Prodigy have steadily grown a number of utility functions to wrap the multiple serialization formats we need to support (especially json, msgpack and pickle). These wrapping functions ended up duplicated across our codebases, so we wanted to put them in one place.
At the same time, we noticed that having a lot of small dependencies was making maintainence harder, and making installation slower. To solve this, we've made srsly standalone, by including the component packages directly within it. This way we can provide all the serialization utilities we need in a single binary wheel.
srsly currently includes forks of the following packages:
ujson
msgpack
msgpack-numpy
cloudpickle
* WIP: replace json/ujson with srsly
* Replace ujson in examples
Use regular json instead of srsly to make code easier to read and follow
* Update requirements
* Fix imports
* Fix typos
* Replace msgpack with srsly
* Fix warning
2018-12-03 03:28:22 +03:00
|
|
|
import srsly
|
2019-11-04 04:38:45 +03:00
|
|
|
from wasabi import msg
|
2017-10-12 21:05:06 +03:00
|
|
|
|
2018-11-30 22:16:14 +03:00
|
|
|
from ..compat import path2str
|
💫 Replace ujson, msgpack and dill/pickle/cloudpickle with srsly (#3003)
Remove hacks and wrappers, keep code in sync across our libraries and move spaCy a few steps closer to only depending on packages with binary wheels 🎉
See here: https://github.com/explosion/srsly
Serialization is hard, especially across Python versions and multiple platforms. After dealing with many subtle bugs over the years (encodings, locales, large files) our libraries like spaCy and Prodigy have steadily grown a number of utility functions to wrap the multiple serialization formats we need to support (especially json, msgpack and pickle). These wrapping functions ended up duplicated across our codebases, so we wanted to put them in one place.
At the same time, we noticed that having a lot of small dependencies was making maintainence harder, and making installation slower. To solve this, we've made srsly standalone, by including the component packages directly within it. This way we can provide all the serialization utilities we need in a single binary wheel.
srsly currently includes forks of the following packages:
ujson
msgpack
msgpack-numpy
cloudpickle
* WIP: replace json/ujson with srsly
* Replace ujson in examples
Use regular json instead of srsly to make code easier to read and follow
* Update requirements
* Fix imports
* Fix typos
* Replace msgpack with srsly
* Fix warning
2018-12-03 03:28:22 +03:00
|
|
|
from ..util import get_data_path
|
2017-10-12 21:05:06 +03:00
|
|
|
from .. import about
|
|
|
|
|
|
|
|
|
2018-01-04 23:33:47 +03:00
|
|
|
def validate():
|
2018-11-30 22:16:14 +03:00
|
|
|
"""
|
|
|
|
Validate that the currently installed version of spaCy is compatible
|
2017-10-12 21:05:06 +03:00
|
|
|
with the installed models. Should be run after `pip install -U spacy`.
|
|
|
|
"""
|
2018-11-30 22:16:14 +03:00
|
|
|
with msg.loading("Loading compatibility table..."):
|
|
|
|
r = requests.get(about.__compatibility__)
|
|
|
|
if r.status_code != 200:
|
2018-12-08 13:49:43 +03:00
|
|
|
msg.fail(
|
|
|
|
"Server error ({})".format(r.status_code),
|
|
|
|
"Couldn't fetch compatibility table.",
|
|
|
|
exits=1,
|
|
|
|
)
|
2018-11-30 22:16:14 +03:00
|
|
|
msg.good("Loaded compatibility table")
|
|
|
|
compat = r.json()["spacy"]
|
2019-03-17 14:16:53 +03:00
|
|
|
version = about.__version__
|
|
|
|
version = version.rsplit(".dev", 1)[0]
|
|
|
|
current_compat = compat.get(version)
|
2018-02-01 00:06:28 +03:00
|
|
|
if not current_compat:
|
2018-11-30 22:16:14 +03:00
|
|
|
msg.fail(
|
2019-03-17 14:16:53 +03:00
|
|
|
"Can't find spaCy v{} in compatibility table".format(version),
|
2018-11-30 22:16:14 +03:00
|
|
|
about.__compatibility__,
|
|
|
|
exits=1,
|
|
|
|
)
|
2017-10-12 21:05:06 +03:00
|
|
|
all_models = set()
|
|
|
|
for spacy_v, models in dict(compat).items():
|
|
|
|
all_models.update(models.keys())
|
|
|
|
for model, model_vs in models.items():
|
|
|
|
compat[spacy_v][model] = [reformat_version(v) for v in model_vs]
|
|
|
|
model_links = get_model_links(current_compat)
|
|
|
|
model_pkgs = get_model_pkgs(current_compat, all_models)
|
2018-11-30 22:16:14 +03:00
|
|
|
incompat_links = {l for l, d in model_links.items() if not d["compat"]}
|
|
|
|
incompat_models = {d["name"] for _, d in model_pkgs.items() if not d["compat"]}
|
|
|
|
incompat_models.update(
|
|
|
|
[d["name"] for _, d in model_links.items() if not d["compat"]]
|
|
|
|
)
|
2017-10-12 21:05:06 +03:00
|
|
|
na_models = [m for m in incompat_models if m not in current_compat]
|
|
|
|
update_models = [m for m in incompat_models if m in current_compat]
|
2018-11-30 22:16:14 +03:00
|
|
|
spacy_dir = Path(__file__).parent.parent
|
|
|
|
|
2018-12-08 13:49:43 +03:00
|
|
|
msg.divider("Installed models (spaCy v{})".format(about.__version__))
|
2018-11-30 22:16:14 +03:00
|
|
|
msg.info("spaCy installation: {}".format(path2str(spacy_dir)))
|
2017-10-12 21:05:06 +03:00
|
|
|
|
|
|
|
if model_links or model_pkgs:
|
2018-11-30 22:16:14 +03:00
|
|
|
header = ("TYPE", "NAME", "MODEL", "VERSION", "")
|
|
|
|
rows = []
|
2017-10-12 21:05:06 +03:00
|
|
|
for name, data in model_pkgs.items():
|
2018-11-30 22:16:14 +03:00
|
|
|
rows.append(get_model_row(current_compat, name, data, msg))
|
2017-10-12 21:05:06 +03:00
|
|
|
for name, data in model_links.items():
|
2018-11-30 22:16:14 +03:00
|
|
|
rows.append(get_model_row(current_compat, name, data, msg, "link"))
|
|
|
|
msg.table(rows, header=header)
|
2017-10-12 21:05:06 +03:00
|
|
|
else:
|
2018-12-08 13:49:43 +03:00
|
|
|
msg.text("No models found in your current environment.", exits=0)
|
2017-10-12 21:05:06 +03:00
|
|
|
if update_models:
|
2018-11-30 22:16:14 +03:00
|
|
|
msg.divider("Install updates")
|
2018-12-08 13:49:43 +03:00
|
|
|
msg.text("Use the following commands to update the model packages:")
|
2018-11-30 22:16:14 +03:00
|
|
|
cmd = "python -m spacy download {}"
|
|
|
|
print("\n".join([cmd.format(pkg) for pkg in update_models]) + "\n")
|
2017-10-12 21:05:06 +03:00
|
|
|
if na_models:
|
2018-11-30 22:16:14 +03:00
|
|
|
msg.text(
|
2018-12-08 13:49:43 +03:00
|
|
|
"The following models are not available for spaCy "
|
|
|
|
"v{}: {}".format(about.__version__, ", ".join(na_models))
|
2018-11-30 22:16:14 +03:00
|
|
|
)
|
2017-10-12 21:05:06 +03:00
|
|
|
if incompat_links:
|
2018-12-08 13:49:43 +03:00
|
|
|
msg.text(
|
|
|
|
"You may also want to overwrite the incompatible links using the "
|
|
|
|
"`python -m spacy link` command with `--force`, or remove them "
|
|
|
|
"from the data directory. "
|
|
|
|
"Data path: {path}".format(path=path2str(get_data_path()))
|
|
|
|
)
|
2018-01-03 23:20:35 +03:00
|
|
|
if incompat_models or incompat_links:
|
|
|
|
sys.exit(1)
|
|
|
|
|
2017-10-12 21:05:06 +03:00
|
|
|
|
|
|
|
def get_model_links(compat):
|
|
|
|
links = {}
|
|
|
|
data_path = get_data_path()
|
|
|
|
if data_path:
|
|
|
|
models = [p for p in data_path.iterdir() if is_model_path(p)]
|
|
|
|
for model in models:
|
2018-11-30 22:16:14 +03:00
|
|
|
meta_path = Path(model) / "meta.json"
|
2017-10-12 21:05:06 +03:00
|
|
|
if not meta_path.exists():
|
|
|
|
continue
|
💫 Replace ujson, msgpack and dill/pickle/cloudpickle with srsly (#3003)
Remove hacks and wrappers, keep code in sync across our libraries and move spaCy a few steps closer to only depending on packages with binary wheels 🎉
See here: https://github.com/explosion/srsly
Serialization is hard, especially across Python versions and multiple platforms. After dealing with many subtle bugs over the years (encodings, locales, large files) our libraries like spaCy and Prodigy have steadily grown a number of utility functions to wrap the multiple serialization formats we need to support (especially json, msgpack and pickle). These wrapping functions ended up duplicated across our codebases, so we wanted to put them in one place.
At the same time, we noticed that having a lot of small dependencies was making maintainence harder, and making installation slower. To solve this, we've made srsly standalone, by including the component packages directly within it. This way we can provide all the serialization utilities we need in a single binary wheel.
srsly currently includes forks of the following packages:
ujson
msgpack
msgpack-numpy
cloudpickle
* WIP: replace json/ujson with srsly
* Replace ujson in examples
Use regular json instead of srsly to make code easier to read and follow
* Update requirements
* Fix imports
* Fix typos
* Replace msgpack with srsly
* Fix warning
2018-12-03 03:28:22 +03:00
|
|
|
meta = srsly.read_json(meta_path)
|
2017-10-12 21:05:06 +03:00
|
|
|
link = model.parts[-1]
|
2018-11-30 22:16:14 +03:00
|
|
|
name = meta["lang"] + "_" + meta["name"]
|
|
|
|
links[link] = {
|
|
|
|
"name": name,
|
|
|
|
"version": meta["version"],
|
|
|
|
"compat": is_compat(compat, name, meta["version"]),
|
|
|
|
}
|
2017-10-12 21:05:06 +03:00
|
|
|
return links
|
|
|
|
|
|
|
|
|
|
|
|
def get_model_pkgs(compat, all_models):
|
2019-10-07 18:22:09 +03:00
|
|
|
import pkg_resources
|
|
|
|
|
2017-10-12 21:05:06 +03:00
|
|
|
pkgs = {}
|
|
|
|
for pkg_name, pkg_data in pkg_resources.working_set.by_key.items():
|
2018-11-30 22:16:14 +03:00
|
|
|
package = pkg_name.replace("-", "_")
|
2017-10-12 21:05:06 +03:00
|
|
|
if package in all_models:
|
|
|
|
version = pkg_data.version
|
2018-11-30 22:16:14 +03:00
|
|
|
pkgs[pkg_name] = {
|
|
|
|
"name": package,
|
|
|
|
"version": version,
|
|
|
|
"compat": is_compat(compat, package, version),
|
|
|
|
}
|
2017-10-12 21:05:06 +03:00
|
|
|
return pkgs
|
|
|
|
|
|
|
|
|
2018-11-30 22:16:14 +03:00
|
|
|
def get_model_row(compat, name, data, msg, model_type="package"):
|
|
|
|
if data["compat"]:
|
|
|
|
comp = msg.text("", color="green", icon="good", no_print=True)
|
|
|
|
version = msg.text(data["version"], color="green", no_print=True)
|
2017-10-12 21:05:06 +03:00
|
|
|
else:
|
2018-11-30 22:16:14 +03:00
|
|
|
version = msg.text(data["version"], color="red", no_print=True)
|
|
|
|
comp = "--> {}".format(compat.get(data["name"], ["n/a"])[0])
|
|
|
|
return (model_type, name, data["name"], version, comp)
|
2017-10-12 21:05:06 +03:00
|
|
|
|
|
|
|
|
|
|
|
def is_model_path(model_path):
|
2018-11-30 22:16:14 +03:00
|
|
|
exclude = ["cache", "pycache", "__pycache__"]
|
2017-10-12 21:05:06 +03:00
|
|
|
name = model_path.parts[-1]
|
2018-11-30 22:16:14 +03:00
|
|
|
return model_path.is_dir() and name not in exclude and not name.startswith(".")
|
2017-10-12 21:05:06 +03:00
|
|
|
|
|
|
|
|
|
|
|
def is_compat(compat, name, version):
|
|
|
|
return name in compat and version in compat[name]
|
|
|
|
|
|
|
|
|
|
|
|
def reformat_version(version):
|
2017-10-27 15:38:39 +03:00
|
|
|
"""Hack to reformat old versions ending on '-alpha' to match pip format."""
|
2018-11-30 22:16:14 +03:00
|
|
|
if version.endswith("-alpha"):
|
|
|
|
return version.replace("-alpha", "a0")
|
|
|
|
return version.replace("-alpha", "a")
|