Skip to content
This repository was archived by the owner on Apr 15, 2025. It is now read-only.
Draft
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
15 changes: 13 additions & 2 deletions src/prisma/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
__copyright__ = 'Copyright 2020-2023 RobertCraigie'
__version__ = '0.15.0'

from typing import TYPE_CHECKING
import sys as _sys
from typing import TYPE_CHECKING, Union

from . import errors as errors
from .utils import setup_logging
Expand All @@ -19,6 +20,13 @@
)
from .validator import *

try:
from .metadata import GENERATED_VERSION as _generated_version # noqa: TID251

GENERATED_VERSION: Union[str, None] = _generated_version
except ImportError:
GENERATED_VERSION = None # pyright: ignore[reportConstantRedefinition]

# the import ordering is important here because
# we rely on the fact that `prisma/client.py` is the
# first piece of generated code that is loaded. This is
Expand Down Expand Up @@ -49,7 +57,6 @@ def __getattr__(name: str):
except KeyError as err:
# TODO: support checking for 'models' here too
if name in {'Prisma', 'Client'}:
# TODO: remove this frame from the stack trace
raise RuntimeError(
"The Client hasn't been generated yet, "
'you must run `prisma generate` before you can use the client.\n'
Expand All @@ -58,6 +65,10 @@ def __getattr__(name: str):

# leaves handling of this potential error to Python as per PEP 562
raise AttributeError() from err
except Exception as exc:
if GENERATED_VERSION == __version__:
raise

print('ignoring error during generated client import as the generated client is outdated', file=_sys.stderr) # noqa: T201

setup_logging()
2 changes: 1 addition & 1 deletion src/prisma/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def __del__(self) -> None:
# client as well as the transaction client the original client cannot
# be `free`d before the transaction is finished. So stopping the engine
# here should be safe.
if self._internal_engine is not None and not self._copied:
if hasattr(self, '_internal_engine') and self._internal_engine is not None and not self._copied:
log.debug('unclosed client - stopping engine')
engine = self._internal_engine
self._internal_engine = None
Expand Down
3 changes: 3 additions & 0 deletions src/prisma/generator/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,12 @@ def parse_obj(cls, obj: Any) -> 'GenericData[ConfigT]':

def to_params(self) -> Dict[str, Any]:
"""Get the parameters that should be sent to Jinja templates"""
from .. import __version__

params = vars(self)
params['type_schema'] = Schema.from_data(self)
params['client_types'] = ClientTypes.from_data(self)
params['generated_version'] = __version__

# add utility functions
for func in [
Expand Down
7 changes: 6 additions & 1 deletion src/prisma/generator/templates/client.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ from typing_extensions import override

from pydantic import BaseModel

from . import types, models, errors, actions
from . import types, models, errors, actions, __version__, metadata
from ._base_client import BasePrisma, UseClientDefault, USE_CLIENT_DEFAULT
from .types import DatasourceOverride, HttpConfig, MetricsFormat
from ._types import BaseModelT, PrismaMethod, TransactionId, Datasource
Expand Down Expand Up @@ -77,6 +77,11 @@ class Prisma({% if is_async %}AsyncBasePrisma{% else %}SyncBasePrisma{% endif %}
connect_timeout: int | timedelta = DEFAULT_CONNECT_TIMEOUT,
http: HttpConfig | None = None,
) -> None:
if metadata.GENERATED_VERSION != __version__:
raise RuntimeError(
f'outdated generated client, the client is disabled until `prisma generate` is ran again; generated version={metadata.GENERATED_VERSION} package version={__version__}'
)

super().__init__(
http=http,
use_dotenv=use_dotenv,
Expand Down
1 change: 1 addition & 0 deletions src/prisma/generator/templates/metadata.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ from __future__ import annotations
# fmt: off
# -- template metadata.py.jinja --

GENERATED_VERSION: str = '{{ generated_version }}'

PRISMA_MODELS: set[str] = {
{% for model in dmmf.datamodel.models %}
Expand Down
14 changes: 13 additions & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from mock import AsyncMock
from pytest_mock import MockerFixture

from prisma import ENGINE_TYPE, SCHEMA_PATH, Prisma, errors, get_client
from prisma import ENGINE_TYPE, SCHEMA_PATH, Prisma, errors, metadata, get_client
from prisma.types import HttpConfig
from prisma.testing import reset_client
from prisma.cli.prisma import run
Expand Down Expand Up @@ -255,3 +255,15 @@ def test_is_registered(client: Prisma) -> None:
with reset_client():
assert not client.is_registered()
assert not other_client.is_registered()


def test_version_mismatch() -> None:
old_version = metadata.GENERATED_VERSION

try:
metadata.GENERATED_VERSION = '0.10.4'

with pytest.raises(RuntimeError, match='outdated generated client'):
Prisma()
finally:
metadata.GENERATED_VERSION = old_version
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ from typing_extensions import override

from pydantic import BaseModel

from . import types, models, errors, actions
from . import types, models, errors, actions, __version__, metadata
from ._base_client import BasePrisma, UseClientDefault, USE_CLIENT_DEFAULT
from .types import DatasourceOverride, HttpConfig, MetricsFormat
from ._types import BaseModelT, PrismaMethod, TransactionId, Datasource
Expand Down Expand Up @@ -133,6 +133,11 @@ class Prisma(AsyncBasePrisma):
connect_timeout: int | timedelta = DEFAULT_CONNECT_TIMEOUT,
http: HttpConfig | None = None,
) -> None:
if metadata.GENERATED_VERSION != __version__:
raise RuntimeError(
f'outdated generated client, the client is disabled until `prisma generate` is ran again; generated version={metadata.GENERATED_VERSION} package version={__version__}'
)

super().__init__(
http=http,
use_dotenv=use_dotenv,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ from __future__ import annotations
# fmt: off
# -- template metadata.py.jinja --

GENERATED_VERSION: str = '0.15.0'

PRISMA_MODELS: set[str] = {
'Post',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ from typing_extensions import override

from pydantic import BaseModel

from . import types, models, errors, actions
from . import types, models, errors, actions, __version__, metadata
from ._base_client import BasePrisma, UseClientDefault, USE_CLIENT_DEFAULT
from .types import DatasourceOverride, HttpConfig, MetricsFormat
from ._types import BaseModelT, PrismaMethod, TransactionId, Datasource
Expand Down Expand Up @@ -133,6 +133,11 @@ class Prisma(SyncBasePrisma):
connect_timeout: int | timedelta = DEFAULT_CONNECT_TIMEOUT,
http: HttpConfig | None = None,
) -> None:
if metadata.GENERATED_VERSION != __version__:
raise RuntimeError(
f'outdated generated client, the client is disabled until `prisma generate` is ran again; generated version={metadata.GENERATED_VERSION} package version={__version__}'
)

super().__init__(
http=http,
use_dotenv=use_dotenv,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ from __future__ import annotations
# fmt: off
# -- template metadata.py.jinja --

GENERATED_VERSION: str = '0.15.0'

PRISMA_MODELS: set[str] = {
'Post',
Expand Down