Skip to content

medcat.utils.envsnapshot

Classes:

Functions:

Attributes:

DEP_NAME_PATTERN module-attribute

DEP_NAME_PATTERN = compile('^[a-zA-Z0-9\\-_]+')

logger module-attribute

logger = getLogger(__name__)

Environment

Bases: BaseModel, AbstractSerialisable

Methods:

Attributes:

cpu_arcitecture instance-attribute

cpu_arcitecture: str

dependencies instance-attribute

dependencies: dict[str, str]

os instance-attribute

os: str

python_version instance-attribute

python_version: str

transitive_deps instance-attribute

transitive_deps: dict[str, str]

get_init_attrs classmethod

get_init_attrs() -> list[str]
Source code in medcat-v2/medcat/utils/envsnapshot.py
140
141
142
@classmethod
def get_init_attrs(cls) -> list[str]:
    return list(cls.model_fields)

get_direct_dependencies

get_direct_dependencies(include_extras: bool) -> list[str]

Gets the direct dependencies of the current package and their versions.

Parameters:

  • include_extras

    (bool) –

    Whether to include extras (like spacy).

Source code in medcat-v2/medcat/utils/envsnapshot.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def get_direct_dependencies(include_extras: bool) -> list[str]:
    """Gets the direct dependencies of the current package and their versions.

    Args:
        include_extras (bool): Whether to include extras (like spacy).
    """
    # NOTE: __package__ would be medcat.utils in this case
    package = __package__.split('.', 1)[0]
    reqs = importlib.metadata.requires(package)
    if reqs is None:
        raise ValueError("Unable to find package direct dependencies")
    # filter out extras
    if not include_extras:
        reqs = [req for req in reqs
                if "; extra ==" not in req]
    # only keep name, not version
    # NOTE: all correct dependency names will match this regex
    reqs = [DEP_NAME_PATTERN.match(req).group(0).lower()  # type: ignore
            for req in reqs]
    return reqs

get_environment_info

get_environment_info(include_transitive_deps: bool = True, include_extras: bool = True) -> Environment

Get the current environment information.

This includes dependency versions, the OS, the CPU architecture and the python version.

Parameters:

  • include_transitive_deps

    (bool, default: True ) –

    Whether to include transitive dependencies. Defaults to True.

  • include_extras

    (bool, default: True ) –

    Whether to include extras (like spacy). Defaults to True.

Returns:

Source code in medcat-v2/medcat/utils/envsnapshot.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
def get_environment_info(include_transitive_deps: bool = True,
                         include_extras: bool = True) -> Environment:
    """Get the current environment information.

    This includes dependency versions, the OS, the CPU architecture and the
        python version.

    Args:
        include_transitive_deps (bool): Whether to include transitive
            dependencies. Defaults to True.
        include_extras (bool): Whether to include extras (like spacy).
            Defaults to True.

    Returns:
        Environment: The environment.
    """
    deps = get_installed_dependencies(include_extras)
    os = platform.platform()
    cpu_arc = platform.machine()
    py_ver = platform.python_version()
    if include_transitive_deps:
        direct_deps = list(deps.keys())
        trans_deps = get_transitive_deps(direct_deps)
    else:
        trans_deps = {}
    return Environment(dependencies=deps, transitive_deps=trans_deps, os=os,
                       cpu_arcitecture=cpu_arc, python_version=py_ver)

get_installed_dependencies

get_installed_dependencies(include_extras: bool) -> dict[str, str]

Get the installed packages and their versions.

Parameters:

  • include_extras

    (bool) –

    Whether to include extras (like spacy).

Returns:

  • dict[str, str]

    dict[str, str]: All installed packages and their versions.

Source code in medcat-v2/medcat/utils/envsnapshot.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def get_installed_dependencies(include_extras: bool) -> dict[str, str]:
    """Get the installed packages and their versions.

    Args:
        include_extras (bool): Whether to include extras (like spacy).

    Returns:
        dict[str, str]: All installed packages and their versions.
    """
    direct_deps = get_direct_dependencies(include_extras)
    installed_packages: dict[str, str] = {}
    for package in importlib.metadata.distributions():
        req_name = package.metadata["name"].lower()
        # NOTE: we're checking against the '-' typed package name not
        #       the import name (which will have _ instead)
        req_name_dashes = req_name.replace("_", "-")
        if all(cn not in direct_deps for cn in
               [req_name, req_name_dashes]):
            continue
        installed_packages[req_name] = package.version
    return installed_packages

get_transitive_deps

get_transitive_deps(direct_deps: list[str]) -> dict[str, str]

Get the transitive dependencies of the direct dependencies.

Parameters:

  • direct_deps

    (list[str]) –

    List of direct dependencies.

Returns:

  • dict[str, str]

    dict[str, str]: The dependency names and their corresponding versions.

Source code in medcat-v2/medcat/utils/envsnapshot.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
def get_transitive_deps(direct_deps: list[str]) -> dict[str, str]:
    """Get the transitive dependencies of the direct dependencies.

    Args:
        direct_deps (list[str]): List of direct dependencies.

    Returns:
        dict[str, str]: The dependency names and their corresponding versions.
    """
    all_deps: dict[str, str] = {}
    to_process = set(direct_deps)
    processed = set()
    # list installed packages for ease of use
    installed_packages = {
        dist.metadata['name'].lower()
        for dist in importlib.metadata.distributions()}

    while to_process:
        package = to_process.pop()
        if package in processed:
            continue

        processed.add(package)

        try:
            dist = importlib.metadata.distribution(package)
        except importlib.metadata.PackageNotFoundError:
            # NOTE: if not installed, we won't bother
            #       after all, if we can save the model, clearly
            #       everything is working
            continue
        requires = dist.requires or []

        for req in requires:
            match = DEP_NAME_PATTERN.match(req)
            if match is None:
                raise ValueError(f"Malformed dependency: {req}")
            dep_name = match.group(0).lower()
            if (dep_name and dep_name not in processed and
                    dep_name in installed_packages):
                all_deps[dep_name] = importlib.metadata.distribution(
                    dep_name).version
                to_process.add(dep_name)

    for direct in direct_deps:
        # remove direct dependencies if they were added
        all_deps.pop(direct, None)
    return all_deps

is_dependency_installed

is_dependency_installed(dependency: str) -> bool

Checks whether a dependency is installed.

This takes into account changes such as '-' vs '_'. For example, typing-extensions is a direct dependency, but its module path will be typing_extension and that's how we can find it as an installed dependency.

Parameters:

  • dependency

    (str) –

    The dependency in question.

Returns:

  • bool ( bool ) –

    Whether the depedency has been installed.

Source code in medcat-v2/medcat/utils/envsnapshot.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
def is_dependency_installed(dependency: str) -> bool:
    """Checks whether a dependency is installed.

    This takes into account changes such as '-' vs '_'.
    For example, `typing-extensions` is a direct dependency,
    but its module path will be `typing_extension` and that's
    how we can find it as an installed dependency.

    Args:
        dependency (str): The dependency in question.

    Returns:
        bool: Whether the depedency has been installed.
    """
    installed_deps = get_installed_dependencies(True)
    dep_name = dependency.lower()
    dep_name_underscores = dependency.replace("-", "_")
    options = [dep_name, dep_name_underscores]
    return any(option in installed_deps for option in options)