Skip to content

Commit

Permalink
pydantic v2 compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
pjbull committed Jul 10, 2023
1 parent 1bfb82c commit 65d7911
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 28 deletions.
5 changes: 3 additions & 2 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# cloudpathlib Changelog

## v0.15.2 (2023-07-09)
- Compatibility with pydantic >= 2.0.0.

## v0.15.1 (2023-06-16)
- Warn if Pydantic >= 2.0.0 since we are not yet compatible.


## v0.15.0 (2023-06-16)

- Changed return type for `CloudPathMeta.__call__` to fix problems with pyright/pylance ([PR #330](https://github.com/drivendataorg/cloudpathlib/pull/330))
Expand Down
7 changes: 4 additions & 3 deletions cloudpathlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,18 @@

class PydanticVersionWarning(UserWarning):
message = (
"This version of cloudpathlib is only compatible with pydantic<2.0.0. "
"This version of cloudpathlib is only compatible with pydantic>=2.0.0. "
"You can ignore this warning if none of your pydantic models are "
"annotated with cloudpathlib types."
"annotated with cloudpathlib types. cloudpathlib=0.15.1 is the last "
"version that supports pydantic<2.0.0."
)


try:
import pydantic
from packaging.version import parse

if parse(pydantic.__version__) >= parse("2.0.0"):
if parse(pydantic.__version__) < parse("2.0.0"):
warn(PydanticVersionWarning(PydanticVersionWarning.message))

except ImportError:
Expand Down
31 changes: 22 additions & 9 deletions cloudpathlib/anypath.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
from abc import ABC
from pathlib import Path
from typing import Union
from typing import Any, Union

from .cloudpath import InvalidPrefixError, CloudPath
from .exceptions import AnyPathTypeError
Expand Down Expand Up @@ -30,18 +30,31 @@ def __new__(cls, *args, **kwargs) -> Union[CloudPath, Path]: # type: ignore
f"Path exception: {repr(path_exception)}"
)

# =========== pydantic integration special methods ===============
@classmethod
def __get_validators__(cls):
def __get_pydantic_core_schema__(cls, _source_type: Any, _handler):
"""Pydantic special method. See
https://pydantic-docs.helpmanual.io/usage/types/#custom-data-types"""
yield cls._validate
https://docs.pydantic.dev/2.0/usage/types/custom/"""
try:
from pydantic_core import core_schema

return core_schema.no_info_after_validator_function(
cls.validate,
core_schema.any_schema(),
)
except ImportError:
return None

@classmethod
def _validate(cls, value) -> Union[CloudPath, Path]:
"""Used as a Pydantic validator. See
https://pydantic-docs.helpmanual.io/usage/types/#custom-data-types"""
# Note __new__ is static method and not a class method
return cls.__new__(cls, value)
def validate(cls, v: str) -> Union[CloudPath, Path]:
"""Pydantic special method. See
https://docs.pydantic.dev/2.0/usage/types/custom/"""
try:
return cls.__new__(cls, v)
except AnyPathTypeError as e:
# type errors no longer converted to validation errors
# https://docs.pydantic.dev/2.0/migration/#typeerror-is-no-longer-converted-to-validationerror-in-validators
raise ValueError(e)


AnyPath.register(CloudPath) # type: ignore
Expand Down
20 changes: 14 additions & 6 deletions cloudpathlib/cloudpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -1113,16 +1113,24 @@ def _upload_file_to_cloud(

# =========== pydantic integration special methods ===============
@classmethod
def __get_validators__(cls) -> Generator[Callable[[Any], Self], None, None]:
def __get_pydantic_core_schema__(cls, _source_type: Any, _handler):
"""Pydantic special method. See
https://pydantic-docs.helpmanual.io/usage/types/#custom-data-types"""
yield cls._validate
https://docs.pydantic.dev/2.0/usage/types/custom/"""
try:
from pydantic_core import core_schema

return core_schema.no_info_after_validator_function(
cls.validate,
core_schema.any_schema(),
)
except ImportError:
return None

@classmethod
def _validate(cls, value: Any) -> Self:
def validate(cls, v: str) -> Self:
"""Used as a Pydantic validator. See
https://pydantic-docs.helpmanual.io/usage/types/#custom-data-types"""
return cls(value)
https://docs.pydantic.dev/2.0/usage/types/custom/"""
return cls(v)


# The function resolve is not available on Pure paths because it removes relative
Expand Down
14 changes: 8 additions & 6 deletions docs/docs/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

## Pydantic

!!! warning
`cloudpathlib` integrates with [Pydantic](https://pydantic-docs.helpmanual.io/)'s data validation. You can declare fields with cloud path classes, and Pydantic's validation mechanisms will run inputs through the cloud path's constructor.

As of version `0.15.1`, `cloudpathlib` is not compatible with
version `2.0.0` or greater of Pydantic. If you want to annotate
your pydantic models with cloudpathlib types, you need to use
`pydantic<2`.
### Compatibility table

`cloudpathlib` integrates with [Pydantic](https://pydantic-docs.helpmanual.io/)'s data validation. You can declare fields with cloud path classes, and Pydantic's validation mechanisms will run inputs through the cloud path's constructor.
| `pydantic` version | `cloudpathlib` version |
| --- | --- |
| `>=2.0.0` | `>=0.15.2` |
| `<2.0.0` | `<0.15.2` |

### Example

```python
from cloudpathlib import S3Path
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi"

[project]
name = "cloudpathlib"
version = "0.15.1"
version = "0.15.2"
description = "pathlib-style classes for cloud storage services."
readme = "README.md"
authors = [{ name = "DrivenData", email = "[email protected]" }]
Expand Down
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ nbautoexport
pandas
pillow
psutil
pydantic<2
pydantic
pytest
pytest-cases
pytest-cov
Expand Down

0 comments on commit 65d7911

Please sign in to comment.