Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazily load cli with module __getattr__ #13721

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 37 additions & 8 deletions spacy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,41 @@
# set library-specific custom warning handling before doing anything else
from .errors import setup_default_warnings

setup_default_warnings() # noqa: E402
setup_default_warnings()

# These are imported as part of the API
from thinc.api import Config, prefer_gpu, require_cpu, require_gpu # noqa: F401
from thinc.api import Config, prefer_gpu, require_cpu, require_gpu

from . import pipeline # noqa: F401
from . import util
from .about import __version__ # noqa: F401
from .cli.info import info # noqa: F401
from . import pipeline, util
from .about import __version__
from .errors import Errors
from .glossary import explain # noqa: F401
from .glossary import explain
from .language import Language
from .util import logger, registry # noqa: F401
from .util import logger, registry
from .vocab import Vocab

if sys.maxunicode == 65535:
raise SystemError(Errors.E130)

__all__ = [
"__version__",
"blank",
"Config",
"Errors",
"explain",
"info",
"Language",
"load",
"logger",
"pipeline",
"prefer_gpu",
"registry",
"require_cpu",
"require_gpu",
"util",
"Vocab",
]


def load(
name: Union[str, Path],
Expand Down Expand Up @@ -77,3 +94,15 @@ def blank(
# We should accept both dot notation and nested dict here for consistency
config = util.dot_to_dict(config)
return LangClass.from_config(config, vocab=vocab, meta=meta)


def __getattr__(name):
if name == "cli":
import importlib

return importlib.import_module("." + name, __name__)
if name == "info":
from .cli.info import info

return info
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
24 changes: 24 additions & 0 deletions spacy/tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,3 +495,27 @@ def test_find_available_port():
with pytest.warns(UserWarning, match="already in use"):
found_port = find_available_port(port, host, auto_select=True)
assert found_port == port + 1, "Didn't find next port"


def test_lazy_load_cli():
import subprocess
import sys
from textwrap import dedent

subprocess.run(
[
sys.executable,
"-c",
dedent(
"""\
import sys
import spacy
assert "spacy" in sys.modules
assert "spacy.cli" not in sys.modules
spacy.cli
assert "spacy.cli" in sys.modules
"""
),
],
check=True,
)