1"""The `version` module holds the version information for Pydantic."""
2
3from __future__ import annotations as _annotations
4
5import sys
6
7from pydantic_core import __version__ as __pydantic_core_version__
8
9__all__ = 'VERSION', 'version_info'
10
11VERSION = '2.12.0'
12"""The version of Pydantic.
13
14This version specifier is guaranteed to be compliant with the [specification],
15introduced by [PEP 440].
16
17[specification]: https://packaging.python.org/en/latest/specifications/version-specifiers/
18[PEP 440]: https://peps.python.org/pep-0440/
19"""
20
21# Keep this in sync with the version constraint in the `pyproject.toml` dependencies:
22_COMPATIBLE_PYDANTIC_CORE_VERSION = '2.41.1'
23
24
25def version_short() -> str:
26 """Return the `major.minor` part of Pydantic version.
27
28 It returns '2.1' if Pydantic version is '2.1.1'.
29 """
30 return '.'.join(VERSION.split('.')[:2])
31
32
33def version_info() -> str:
34 """Return complete version information for Pydantic and its dependencies."""
35 import importlib.metadata
36 import platform
37 from pathlib import Path
38
39 import pydantic_core._pydantic_core as pdc
40
41 from ._internal import _git as git
42
43 # get data about packages that are closely related to pydantic, use pydantic or often conflict with pydantic
44 package_names = {
45 'email-validator',
46 'fastapi',
47 'mypy',
48 'pydantic-extra-types',
49 'pydantic-settings',
50 'pyright',
51 'typing_extensions',
52 }
53 related_packages = []
54
55 for dist in importlib.metadata.distributions():
56 name = dist.metadata['Name']
57 if name in package_names:
58 related_packages.append(f'{name}-{dist.version}')
59
60 pydantic_dir = Path(__file__).parents[1].resolve()
61 most_recent_commit = (
62 git.git_revision(pydantic_dir) if git.is_git_repo(pydantic_dir) and git.have_git() else 'unknown'
63 )
64
65 info = {
66 'pydantic version': VERSION,
67 'pydantic-core version': __pydantic_core_version__,
68 'pydantic-core build': getattr(pdc, 'build_info', None) or pdc.build_profile, # pyright: ignore[reportPrivateImportUsage]
69 'python version': sys.version,
70 'platform': platform.platform(),
71 'related packages': ' '.join(related_packages),
72 'commit': most_recent_commit,
73 }
74 return '\n'.join('{:>30} {}'.format(k + ':', str(v).replace('\n', ' ')) for k, v in info.items())
75
76
77def check_pydantic_core_version() -> bool:
78 """Check that the installed `pydantic-core` dependency is compatible."""
79 return __pydantic_core_version__ == _COMPATIBLE_PYDANTIC_CORE_VERSION
80
81
82def _ensure_pydantic_core_version() -> None: # pragma: no cover
83 if not check_pydantic_core_version():
84 raise_error = True
85 # Do not raise the error if pydantic is installed in editable mode (i.e. in development):
86 if sys.version_info >= (3, 13): # origin property added in 3.13
87 from importlib.metadata import distribution
88
89 dist = distribution('pydantic')
90 if getattr(getattr(dist.origin, 'dir_info', None), 'editable', False):
91 raise_error = False
92
93 if raise_error:
94 raise SystemError(
95 f'The installed pydantic-core version ({__pydantic_core_version__}) is incompatible '
96 f'with the current pydantic version, which requires {_COMPATIBLE_PYDANTIC_CORE_VERSION}. '
97 "If you encounter this error, make sure that you haven't upgraded pydantic-core manually."
98 )
99
100
101def parse_mypy_version(version: str) -> tuple[int, int, int]:
102 """Parse `mypy` string version to a 3-tuple of ints.
103
104 It parses normal version like `1.11.0` and extra info followed by a `+` sign
105 like `1.11.0+dev.d6d9d8cd4f27c52edac1f537e236ec48a01e54cb.dirty`.
106
107 Args:
108 version: The mypy version string.
109
110 Returns:
111 A triple of ints, e.g. `(1, 11, 0)`.
112 """
113 return tuple(map(int, version.partition('+')[0].split('.'))) # pyright: ignore[reportReturnType]