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

Explicitly raise an error if field names clashes with types #8243

Merged
merged 9 commits into from Jan 15, 2024

Conversation

Viicos
Copy link
Contributor

@Viicos Viicos commented Nov 28, 2023

Change Summary

Linked: #8240.

I'm raising inside from_annotated_attribute so I don't have to duplicate the logic for dataclass, BaseModel etc. But unfortunately I can't provide info to the user regarding the field causing the issue :/

Related issue number

Checklist

  • The pull request title is a good summary of the changes - it will be used in the changelog
  • Unit tests for the changes exist
  • Tests pass on CI
  • Documentation reflects the changes where applicable
  • My PR is ready to review, please add a comment including the phrase "please review" to assign reviewers

Copy link

codspeed-hq bot commented Nov 28, 2023

CodSpeed Performance Report

Merging #8243 will not alter performance

Comparing Viicos:error-name-clashes (b0231e1) with main (2e459bb)

Summary

✅ 10 untouched benchmarks

@Viicos
Copy link
Contributor Author

Viicos commented Nov 28, 2023

This unfortunately fails with the following test:

def test_type_on_annotation():
class FooBar:
pass
class Model(BaseModel):
a: int = int
b: Type[int]
c: Type[int] = int
d: FooBar = FooBar
e: Type[FooBar]
f: Type[FooBar] = FooBar
g: Sequence[Type[FooBar]] = [FooBar]
h: Union[Type[FooBar], Sequence[Type[FooBar]]] = FooBar
i: Union[Type[FooBar], Sequence[Type[FooBar]]] = [FooBar]

Although a: int = int and d: FooBar = FooBar doesn't really make sense, this PR will break any existing code similar to this. So raising an error here might be tricky to implement.

@@ -321,6 +321,14 @@ class MyModel(pydantic.BaseModel):
spam: Annotated[int, pydantic.Field(gt=4)] = 4 # <-- or this
```
"""

if annotation is default:
raise RuntimeError(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should use a pydantic user error with a link to a section describing the problem and solution.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Viicos,

Circling back to this solution - I do think we should go ahead and implement something along the lines of what you have here. Could you use a PydanticUserError here instead, as Samuel mentioned?

Happy to review once you make that change 👍

Copy link
Contributor Author

@Viicos Viicos Jan 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think for now this would be the best we can do. It doesn't cover all cases (e.g. this and this) but this is still better than nothing. Will apply changes soon. Regarding an actual workaround for this issue, the idea from alex could probably work although as I said it introduces a significant amount of complexity

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Yep, let's stick with this for now, then can consider a more complex solution in the future.

@samuelcolvin
Copy link
Member

It would also be nice if we could provide a useful error for the case of

class City(BaseModel):
    date: int = 1
    foo: date

@sydney-runkle
Copy link
Member

Do we want to consider a case like second one here as well?

from datetime import date
from typing import Union

from pydantic import BaseModel, Field

class Foo(BaseModel):
    date: date = Field(default_factory=date.today)

f = Foo()
#> RecursionError: maximum recursion depth exceeded while getting the repr of an object

class FooWithUnion(BaseModel):
    date: Union[date, None] = Field(default=None)

f = FooWithUnion()
#> RecursionError: maximum recursion depth exceeded while getting the repr of an object

@Viicos
Copy link
Contributor Author

Viicos commented Nov 28, 2023

Thanks for the feedback, I'll try to come up with a solution covering all cases. Hopefully it won't involves hacky stuff as this might get tricky

@Viicos
Copy link
Contributor Author

Viicos commented Nov 29, 2023

Do we want to consider a case like second one here as well?

@sydney-runkle where you able to run this code snippet? Union is a special form and the arguments are checked to be sure they are a type. I got the following on my end: TypeError: Union[arg, ...]: each arg must be a type. Got FieldInfo ...

Note that setting a default value of None will pass:

from datetime import date
from typing import Union

from pydantic import BaseModel

class FooWithUnion(BaseModel):
    date: Union[date, None] = None

FooWithUnion.model_json_schema()
#> {'properties': {'date': {'default': None, 'title': 'Date', 'type': 'null'}}, 'title': 'B', 'type': 'object'}
# ^ type: null!

And in this case the issue is silently ignored, so having an explicit error will be beneficial in this case as well

@sydney-runkle
Copy link
Member

@Viicos,

You're right, I get the following:

from datetime import date
from typing import Union

from pydantic import BaseModel, Field

class FooWithUnion(BaseModel):
    date: Union[date, None] = Field(default=None)

f = FooWithUnion()
#> Union[arg, ...]: each arg must be a type. Got FieldInfo(annotation=NoneType, required=False).

Agreed, a more explicit error would be helpful 😄

@Viicos
Copy link
Contributor Author

Viicos commented Nov 29, 2023

The only way that comes to mind currently (that could support all provided cases) would be to parse the AST, which is less than ideal (performance wise, and it adds maintenance burden). Maybe @alexmojaki you have a better/clever idea?

Or maybe comparing field names against the global module namespace? But the case provided here won't be taken into account

@sydney-runkle
Copy link
Member

Makes sense. Curious to hear what @alexmojaki thinks as well.

The case that I mentioned isn't as urgent. As long as we cover the original case and the case that @samuelcolvin mentioned above, that's a pretty significant improvement over what we have now.

@alexmojaki
Copy link
Contributor

I'm flattered 😄

Here's an idea of how it might be possible to actually fix the problem: main...alexmojaki:pydantic:field-type-clash

@Viicos
Copy link
Contributor Author

Viicos commented Nov 29, 2023

Again, @alexmojaki really clever solution. I've played a bit and this looks promising. I'll continue experimenting and will try to come up with an implementation. Thanks again!

Edit: to be clear, this would fix the problem in the first place, which may still not be desirable. But the fix proposed here seems straightforward to implement so it's worth trying imo

@Viicos
Copy link
Contributor Author

Viicos commented Nov 30, 2023

So I've played a bit with it, here is an implementation following @alexmojaki's idea: main...Viicos:pydantic:allow-name-clashes

To be able to support more than FieldInfo as a default value, I'm mangling all attributes (except __annotations__).

That's why I had to alter two things:

  • We need a custom __setitem__ __getitem__ in case class attributes are accessed in the body:
class Model(BaseModel):
    def func(self):
        pass

    func2 = func  # Python will try to get "func" from our custom namespace, where "func" is still prefixed/mangled
  • Do not "override" the custom namespace by a plain dict in dynamic model creations.

All tests passes with this implementation, but testing that it can actually use name that clashes is harder as it does not work inside functions (see here).

I'll let maintainers decide if they want to support this and continue with this solution, otherwise we can continue on this PR and try to see how we could raise better errors.

@alexmojaki
Copy link
Contributor

Does this work with postponed/string annotations? Maybe to support using non-field variables in the class body (which I didn't) you could keep localns in get_cls_type_hints_lenient but exclude model_fields.

We need a custom __setitem__ in case class attributes are accessed in the body:

Do you mean __getitem__? If so, does this mean the __getitem__ is called when evaluating normal expressions in the class body, but not when evaluating annotations? That's weird.

All tests passes with this implementation, but testing that it can actually use name that clashes is harder as it does not work inside functions (see #7327 (comment)).

Is it fair to say that in every case where it doesn't work inside functions, it has never worked, and it also doesn't work with plain classes or standard dataclasses?

You could maybe make it work inside functions if you're willing to take the black magic further:

def foo():
  class A:
    pass

  class Meta(type):
    @classmethod
    def __prepare__(metacls, name, bases):
      import inspect
      frame = inspect.currentframe().f_back
      return dict(frame.f_locals)

  class B(metaclass=Meta):
    A: A

  return B


print(foo().__annotations__)

Or just use one of those test fixtures that makes a module or something.

@Viicos
Copy link
Contributor Author

Viicos commented Nov 30, 2023

Do you mean __getitem__? If so, does this mean the __getitem__ is called when evaluating normal expressions in the class body, but not when evaluating annotations? That's weird.

Yes sorry I meant __getitem__. Actually I've just noticed it is also called for annotations, which defeats the purpose of having this name mangling in the first place (my current implementation doesn't solve the issue currently). So supporting other assignments than FieldInfo might be too risky.

Is it fair to say that in every case where it doesn't work inside functions, it has never worked, and it also doesn't work with plain classes or standard dataclasses?

Yes iirc Python uses either the local namespace or the module namespace. It was discussed somewhere but can't find the related issue.

@alexmojaki
Copy link
Contributor

alexmojaki commented Nov 30, 2023

import inspect
from datetime import date


class D(dict):
    def __getitem__(self, key):
        if not key[:2] == '__' == key[-2:]:
            frame = inspect.currentframe().f_back
            assert frame.f_locals is self

            def namespaces():
                # Get namespaces from enclosing scopes
                outer_frame = frame
                while outer_frame.f_back and outer_frame.f_code in outer_frame.f_back.f_code.co_consts:
                    outer_frame = outer_frame.f_back
                    yield outer_frame.f_locals

                yield frame.f_globals
                yield frame.f_builtins

            for namespace in namespaces():
                try:
                    value = namespace[key]
                except KeyError:
                    pass
                else:
                    if key in self:
                        print(f'Found {key=} in outer namespace, using that instead of value in class')
                    return value
        return super().__getitem__(key)


class M(type):
    @classmethod
    def __prepare__(metacls, name, bases):
        return D()


def bar():
    # Check that we can access variables from enclosing scopes
    # even if Python doesn't create a normal closure for them.
    E = 10

    # Check that inner scopes take precedence over enclosing scopes.
    A = 11

    def foo():
        try:
            # Verify that no closure was created for E
            # Replacing the line below with just `E` makes a closure and fails the assertion.
            # Same for replacing `E: E` further down with `e: E`.
            eval('E')
        except NameError:
            pass
        else:
            raise AssertionError('E should not be defined in foo')

        A = 12

        class C_(metaclass=M):
            B = 13

            A: A = 1
            b: B = 2
            date: date = 3
            int: int = 4
            x: int = 5
            y: int = x
            E: E = 6

        return C_

    C = foo()
    # print(C.A, C.b, C.date, C.int, C.x, C.y)
    # print(C.__annotations__)
    assert C.A == 1
    assert C.b == 2
    assert C.date == 3
    assert C.int == 4
    assert C.x == 5
    assert C.y == 5
    assert C.__annotations__ == {
        'A': 12,
        'b': 13,
        'date': date,
        'int': int,
        'x': int,
        'y': int,
        'E': 10,
    }


def spam():
    # Check that stack frames that aren't enclosing scopes aren't used.
    date = 99

    bar()


spam()

@alexmojaki
Copy link
Contributor

To explain the above code (which I've just updated): we can use the custom namespace to override Python's usual scoping rules and check for a local class variable last (i.e. after enclosing scopes, globals, and builtins) rather than first. Of course that could be weird and surprising in some cases, but we can easily emit warnings/errors when relevant so that the user can see what's happening, and this can be as configurable as we want so that upgrading is still safe. I can explain more but right now I need to go to sleep 😆

@Viicos
Copy link
Contributor Author

Viicos commented Dec 5, 2023

This looks interesting but I don't know if this level of complexity will be accepted? :/

@@ -321,6 +321,14 @@ class MyModel(pydantic.BaseModel):
spam: Annotated[int, pydantic.Field(gt=4)] = 4 # <-- or this
```
"""

if annotation is default:
raise RuntimeError(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Viicos,

Circling back to this solution - I do think we should go ahead and implement something along the lines of what you have here. Could you use a PydanticUserError here instead, as Samuel mentioned?

Happy to review once you make that change 👍

@sydney-runkle
Copy link
Member

@Viicos,

I know you're busy (helping us with other things, partially 🚀 ), but if you have any time to make a minor change here, we'd love to get this change in with the 2.6.0 release early next week 💯.

@Viicos
Copy link
Contributor Author

Viicos commented Jan 12, 2024

I know you're busy (helping us with other things, partially 🚀 ), but if you have any time to make a minor change here, we'd love to get this change in with the 2.6.0 release early next week 💯.

Will take a look this evening/weekend!

Comment on lines 1377 to -1386
class Model(BaseModel):
a: int = int
b: Type[int]
c: Type[int] = int
d: FooBar = FooBar
e: Type[FooBar]
f: Type[FooBar] = FooBar
g: Sequence[Type[FooBar]] = [FooBar]
h: Union[Type[FooBar], Sequence[Type[FooBar]]] = FooBar
i: Union[Type[FooBar], Sequence[Type[FooBar]]] = [FooBar]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As said here: I just removed the now invalid cases

Copy link
Member

@sydney-runkle sydney-runkle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great :), thanks!

docs/errors/usage_errors.md Outdated Show resolved Hide resolved
@sydney-runkle sydney-runkle added the relnotes-change Used for changes to existing functionality which don't have a better categorization. label Jan 15, 2024
@sydney-runkle sydney-runkle mentioned this pull request Jan 15, 2024
10 tasks
@Viicos
Copy link
Contributor Author

Viicos commented Jan 15, 2024

Regarding the other mentioned use cases, it could also be implemented (more easily) in a linter plugin, e.g. with flake8 or ruff. There are some interesting rules that could also be applied, e.g. enforcing field: Annotated[<type>, Field(...)] instead of field: <type> = Field(...), or any use case where people with large code bases would like to enforce consistency.

@sydney-runkle
Copy link
Member

@Viicos,

Looks like a test is still failing - could you please take a look at that before we merge?

@sydney-runkle
Copy link
Member

sydney-runkle commented Jan 15, 2024

@Viicos,

Hmm, still some linting and testing issues - are the make commands working for you locally?

@sydney-runkle sydney-runkle merged commit 5de67f7 into pydantic:main Jan 15, 2024
54 checks passed
@Viicos Viicos deleted the error-name-clashes branch January 15, 2024 16:50
jsuchenia pushed a commit to jsuchenia/adventofcode that referenced this pull request Feb 13, 2024
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [pydantic](https://github.com/pydantic/pydantic) ([changelog](https://docs.pydantic.dev/latest/changelog/)) | dependencies | minor | `2.5.3` -> `2.6.1` |

---

### Release Notes

<details>
<summary>pydantic/pydantic (pydantic)</summary>

### [`v2.6.1`](https://github.com/pydantic/pydantic/blob/HEAD/HISTORY.md#v261-2024-02-05)

[Compare Source](pydantic/pydantic@v2.6.0...v2.6.1)

[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.1)

##### What's Changed

##### Packaging

-   Upgrade to `pydantic-core` 2.16.2 by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8717](pydantic/pydantic#8717)

##### Fixes

-   Fix bug with `mypy` plugin and `no_strict_optional = True` by [@&#8203;dmontagu](https://github.com/dmontagu) in [#&#8203;8666](pydantic/pydantic#8666)
-   Fix `ByteSize` error `type` change by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8681](pydantic/pydantic#8681)
-   Fix inheriting `Field` annotations in dataclasses by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8679](pydantic/pydantic#8679)
-   Fix regression in core schema generation for indirect definition references by [@&#8203;dmontagu](https://github.com/dmontagu) in [#&#8203;8702](pydantic/pydantic#8702)
-   Fix unsupported types bug with `PlainValidator` by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8710](pydantic/pydantic#8710)
-   Reverting problematic fix from 2.6 release, fixing schema building bug by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8718](pydantic/pydantic#8718)
-   Fix warning for tuple of wrong size in `Union` by [@&#8203;davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1174](pydantic/pydantic-core#1174)
-   Fix `computed_field` JSON serializer `exclude_none` behavior by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1187](pydantic/pydantic-core#1187)

### [`v2.6.0`](https://github.com/pydantic/pydantic/blob/HEAD/HISTORY.md#v260-2024-01-23)

[Compare Source](pydantic/pydantic@v2.5.3...v2.6.0)

[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.0)

The code released in v2.6.0 is practically identical to that of v2.6.0b1.

##### What's Changed

##### Packaging

-   Check for `email-validator` version >= 2.0 by [@&#8203;commonism](https://github.com/commonism) in [#&#8203;6033](pydantic/pydantic#6033)
-   Upgrade \`ruff\`\` target version to Python 3.8 by [@&#8203;Elkiwa](https://github.com/Elkiwa) in [#&#8203;8341](pydantic/pydantic#8341)
-   Update to `pydantic-extra-types==2.4.1` by [@&#8203;yezz123](https://github.com/yezz123) in [#&#8203;8478](pydantic/pydantic#8478)
-   Update to `pyright==1.1.345` by [@&#8203;Viicos](https://github.com/Viicos) in [#&#8203;8453](pydantic/pydantic#8453)
-   Update pydantic-core from 2.14.6 to 2.16.1, significant changes from these updates are described below, full changelog [here](pydantic/pydantic-core@v2.14.6...v2.16.1)

##### New Features

-   Add `NatsDsn` by [@&#8203;ekeew](https://github.com/ekeew) in [#&#8203;6874](pydantic/pydantic#6874)
-   Add `ConfigDict.ser_json_inf_nan` by [@&#8203;davidhewitt](https://github.com/davidhewitt) in [#&#8203;8159](pydantic/pydantic#8159)
-   Add `types.OnErrorOmit` by [@&#8203;adriangb](https://github.com/adriangb) in [#&#8203;8222](pydantic/pydantic#8222)
-   Support `AliasGenerator` usage by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8282](pydantic/pydantic#8282)
-   Add Pydantic People Page to docs by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8345](pydantic/pydantic#8345)
-   Support `yyyy-MM-DD` datetime parsing by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8404](pydantic/pydantic#8404)
-   Added bits conversions to the `ByteSize` class [#&#8203;8415](pydantic/pydantic#8415) by [@&#8203;luca-matei](https://github.com/luca-matei) in [#&#8203;8507](pydantic/pydantic#8507)
-   Enable json schema creation with type `ByteSize` by [@&#8203;geospackle](https://github.com/geospackle) in [#&#8203;8537](pydantic/pydantic#8537)
-   Add `eval_type_backport` to handle union operator and builtin generic subscripting in older Pythons by [@&#8203;alexmojaki](https://github.com/alexmojaki) in [#&#8203;8209](pydantic/pydantic#8209)
-   Add support for `dataclass` fields `init` by [@&#8203;dmontagu](https://github.com/dmontagu) in [#&#8203;8552](pydantic/pydantic#8552)
-   Implement pickling for `ValidationError` by [@&#8203;davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1119](pydantic/pydantic-core#1119)
-   Add unified tuple validator that can handle "variadic" tuples via PEP-646 by [@&#8203;dmontagu](https://github.com/dmontagu) in [pydantic/pydantic-core#865](pydantic/pydantic-core#865)

##### Changes

-   Drop Python3.7 support by [@&#8203;hramezani](https://github.com/hramezani) in [#&#8203;7188](pydantic/pydantic#7188)
-   Drop Python 3.7, and PyPy 3.7 and 3.8 by [@&#8203;davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1129](pydantic/pydantic-core#1129)
-   Use positional-only `self` in `BaseModel` constructor, so no field name can ever conflict with it by [@&#8203;ariebovenberg](https://github.com/ariebovenberg) in [#&#8203;8072](pydantic/pydantic#8072)
-   Make `@validate_call` return a function instead of a custom descriptor - fixes binding issue with inheritance and adds `self/cls` argument to validation errors by [@&#8203;alexmojaki](https://github.com/alexmojaki) in [#&#8203;8268](pydantic/pydantic#8268)
-   Exclude `BaseModel` docstring from JSON schema description by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8352](pydantic/pydantic#8352)
-   Introducing `classproperty` decorator for `model_computed_fields` by [@&#8203;Jocelyn-Gas](https://github.com/Jocelyn-Gas) in [#&#8203;8437](pydantic/pydantic#8437)
-   Explicitly raise an error if field names clashes with types by [@&#8203;Viicos](https://github.com/Viicos) in [#&#8203;8243](pydantic/pydantic#8243)
-   Use stricter serializer for unions of simple types by [@&#8203;alexdrydew](https://github.com/alexdrydew) [pydantic/pydantic-core#1132](pydantic/pydantic-core#1132)

##### Performance

-   Add Codspeed profiling Actions workflow  by [@&#8203;lambertsbennett](https://github.com/lambertsbennett) in [#&#8203;8054](pydantic/pydantic#8054)
-   Improve `int` extraction by [@&#8203;samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1155](pydantic/pydantic-core#1155)
-   Improve performance of recursion guard by [@&#8203;samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1156](pydantic/pydantic-core#1156)
-   `dataclass` serialization speedups by [@&#8203;samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1162](pydantic/pydantic-core#1162)
-   Avoid `HashMap` creation when looking up small JSON objects in `LazyIndexMaps` by [@&#8203;samuelcolvin](https://github.com/samuelcolvin) in [pydantic/jiter#55](pydantic/jiter#55)
-   use hashbrown to speedup python string caching by [@&#8203;davidhewitt](https://github.com/davidhewitt) in [pydantic/jiter#51](pydantic/jiter#51)
-   Replace `Peak` with more efficient `Peek` by [@&#8203;davidhewitt](https://github.com/davidhewitt) in [pydantic/jiter#48](pydantic/jiter#48)

##### Fixes

-   Move `getattr` warning in deprecated `BaseConfig` by [@&#8203;tlambert03](https://github.com/tlambert03) in [#&#8203;7183](pydantic/pydantic#7183)
-   Only hash `model_fields`, not whole `__dict__` by [@&#8203;alexmojaki](https://github.com/alexmojaki) in [#&#8203;7786](pydantic/pydantic#7786)
-   Fix mishandling of unions while freezing types in the `mypy` plugin by [@&#8203;dmontagu](https://github.com/dmontagu) in [#&#8203;7411](pydantic/pydantic#7411)
-   Fix `mypy` error on untyped `ClassVar` by [@&#8203;vincent-hachin-wmx](https://github.com/vincent-hachin-wmx) in [#&#8203;8138](pydantic/pydantic#8138)
-   Only compare pydantic fields in `BaseModel.__eq__` instead of whole `__dict__` by [@&#8203;QuentinSoubeyranAqemia](https://github.com/QuentinSoubeyranAqemia) in [#&#8203;7825](pydantic/pydantic#7825)
-   Update `strict` docstring in `model_validate` method. by [@&#8203;LukeTonin](https://github.com/LukeTonin) in [#&#8203;8223](pydantic/pydantic#8223)
-   Fix overload position of `computed_field` by [@&#8203;Viicos](https://github.com/Viicos) in [#&#8203;8227](pydantic/pydantic#8227)
-   Fix custom type type casting used in multiple attributes by [@&#8203;ianhfc](https://github.com/ianhfc) in [#&#8203;8066](pydantic/pydantic#8066)
-   Fix issue not allowing `validate_call` decorator to be dynamically assigned to a class method by [@&#8203;jusexton](https://github.com/jusexton) in [#&#8203;8249](pydantic/pydantic#8249)
-   Fix issue `unittest.mock` deprecation warnings  by [@&#8203;ibleedicare](https://github.com/ibleedicare) in [#&#8203;8262](pydantic/pydantic#8262)
-   Added tests for the case `JsonValue` contains subclassed primitive values by [@&#8203;jusexton](https://github.com/jusexton) in [#&#8203;8286](pydantic/pydantic#8286)
-   Fix `mypy` error on free before validator (classmethod) by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8285](pydantic/pydantic#8285)
-   Fix `to_snake` conversion by [@&#8203;jevins09](https://github.com/jevins09) in [#&#8203;8316](pydantic/pydantic#8316)
-   Fix type annotation of `ModelMetaclass.__prepare__` by [@&#8203;slanzmich](https://github.com/slanzmich) in [#&#8203;8305](pydantic/pydantic#8305)
-   Disallow `config` specification when initializing a `TypeAdapter` when the annotated type has config already by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8365](pydantic/pydantic#8365)
-   Fix a naming issue with JSON schema for generics parametrized by recursive type aliases by [@&#8203;dmontagu](https://github.com/dmontagu) in [#&#8203;8389](pydantic/pydantic#8389)
-   Fix type annotation in pydantic people script by [@&#8203;shenxiangzhuang](https://github.com/shenxiangzhuang) in [#&#8203;8402](pydantic/pydantic#8402)
-   Add support for field `alias` in `dataclass` signature by [@&#8203;NeevCohen](https://github.com/NeevCohen) in [#&#8203;8387](pydantic/pydantic#8387)
-   Fix bug with schema generation with `Field(...)` in a forward ref by [@&#8203;dmontagu](https://github.com/dmontagu) in [#&#8203;8494](pydantic/pydantic#8494)
-   Fix ordering of keys in `__dict__` with `model_construct` call by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8500](pydantic/pydantic#8500)
-   Fix module `path_type` creation when globals does not contain `__name__` by [@&#8203;hramezani](https://github.com/hramezani) in [#&#8203;8470](pydantic/pydantic#8470)
-   Fix for namespace issue with dataclasses with `from __future__ import annotations` by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8513](pydantic/pydantic#8513)
-   Fix: make function validator types positional-only by [@&#8203;pmmmwh](https://github.com/pmmmwh) in [#&#8203;8479](pydantic/pydantic#8479)
-   Fix usage of `@deprecated` by [@&#8203;Viicos](https://github.com/Viicos) in [#&#8203;8294](pydantic/pydantic#8294)
-   Add more support for private attributes in `model_construct` call by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8525](pydantic/pydantic#8525)
-   Use a stack for the types namespace by [@&#8203;dmontagu](https://github.com/dmontagu) in [#&#8203;8378](pydantic/pydantic#8378)
-   Fix schema-building bug with `TypeAliasType` for types with refs by [@&#8203;dmontagu](https://github.com/dmontagu) in [#&#8203;8526](pydantic/pydantic#8526)
-   Support `pydantic.Field(repr=False)` in dataclasses by [@&#8203;tigeryy2](https://github.com/tigeryy2) in [#&#8203;8511](pydantic/pydantic#8511)
-   Override `dataclass_transform` behavior for `RootModel` by [@&#8203;Viicos](https://github.com/Viicos) in [#&#8203;8163](pydantic/pydantic#8163)
-   Refactor signature generation for simplicity by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [#&#8203;8572](pydantic/pydantic#8572)
-   Fix ordering bug of PlainValidator annotation by [@&#8203;Anvil](https://github.com/Anvil) in [#&#8203;8567](pydantic/pydantic#8567)
-   Fix `exclude_none` for json serialization of `computed_field`s by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1098](pydantic/pydantic-core#1098)
-   Support yyyy-MM-DD string for datetimes by [@&#8203;sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1124](pydantic/pydantic-core#1124)
-   Tweak ordering of definitions in generated schemas by [@&#8203;StrawHatDrag0n](https://github.com/StrawHatDrag0n) in [#&#8203;8583](pydantic/pydantic#8583)

##### New Contributors

##### `pydantic`

-   [@&#8203;ekeew](https://github.com/ekeew) made their first contribution in [#&#8203;6874](pydantic/pydantic#6874)
-   [@&#8203;lambertsbennett](https://github.com/lambertsbennett) made their first contribution in [#&#8203;8054](pydantic/pydantic#8054)
-   [@&#8203;vincent-hachin-wmx](https://github.com/vincent-hachin-wmx) made their first contribution in [#&#8203;8138](pydantic/pydantic#8138)
-   [@&#8203;QuentinSoubeyranAqemia](https://github.com/QuentinSoubeyranAqemia) made their first contribution in [#&#8203;7825](pydantic/pydantic#7825)
-   [@&#8203;ariebovenberg](https://github.com/ariebovenberg) made their first contribution in [#&#8203;8072](pydantic/pydantic#8072)
-   [@&#8203;LukeTonin](https://github.com/LukeTonin) made their first contribution in [#&#8203;8223](pydantic/pydantic#8223)
-   [@&#8203;denisart](https://github.com/denisart) made their first contribution in [#&#8203;8231](pydantic/pydantic#8231)
-   [@&#8203;ianhfc](https://github.com/ianhfc) made their first contribution in [#&#8203;8066](pydantic/pydantic#8066)
-   [@&#8203;eonu](https://github.com/eonu) made their first contribution in [#&#8203;8255](pydantic/pydantic#8255)
-   [@&#8203;amandahla](https://github.com/amandahla) made their first contribution in [#&#8203;8263](pydantic/pydantic#8263)
-   [@&#8203;ibleedicare](https://github.com/ibleedicare) made their first contribution in [#&#8203;8262](pydantic/pydantic#8262)
-   [@&#8203;jevins09](https://github.com/jevins09) made their first contribution in [#&#8203;8316](pydantic/pydantic#8316)
-   [@&#8203;cuu508](https://github.com/cuu508) made their first contribution in [#&#8203;8322](pydantic/pydantic#8322)
-   [@&#8203;slanzmich](https://github.com/slanzmich) made their first contribution in [#&#8203;8305](pydantic/pydantic#8305)
-   [@&#8203;jensenbox](https://github.com/jensenbox) made their first contribution in [#&#8203;8331](pydantic/pydantic#8331)
-   [@&#8203;szepeviktor](https://github.com/szepeviktor) made their first contribution in [#&#8203;8356](pydantic/pydantic#8356)
-   [@&#8203;Elkiwa](https://github.com/Elkiwa) made their first contribution in [#&#8203;8341](pydantic/pydantic#8341)
-   [@&#8203;parhamfh](https://github.com/parhamfh) made their first contribution in [#&#8203;8395](pydantic/pydantic#8395)
-   [@&#8203;shenxiangzhuang](https://github.com/shenxiangzhuang) made their first contribution in [#&#8203;8402](pydantic/pydantic#8402)
-   [@&#8203;NeevCohen](https://github.com/NeevCohen) made their first contribution in [#&#8203;8387](pydantic/pydantic#8387)
-   [@&#8203;zby](https://github.com/zby) made their first contribution in [#&#8203;8497](pydantic/pydantic#8497)
-   [@&#8203;patelnets](https://github.com/patelnets) made their first contribution in [#&#8203;8491](pydantic/pydantic#8491)
-   [@&#8203;edwardwli](https://github.com/edwardwli) made their first contribution in [#&#8203;8503](pydantic/pydantic#8503)
-   [@&#8203;luca-matei](https://github.com/luca-matei) made their first contribution in [#&#8203;8507](pydantic/pydantic#8507)
-   [@&#8203;Jocelyn-Gas](https://github.com/Jocelyn-Gas) made their first contribution in [#&#8203;8437](pydantic/pydantic#8437)
-   [@&#8203;bL34cHig0](https://github.com/bL34cHig0) made their first contribution in [#&#8203;8501](pydantic/pydantic#8501)
-   [@&#8203;tigeryy2](https://github.com/tigeryy2) made their first contribution in [#&#8203;8511](pydantic/pydantic#8511)
-   [@&#8203;geospackle](https://github.com/geospackle) made their first contribution in [#&#8203;8537](pydantic/pydantic#8537)
-   [@&#8203;Anvil](https://github.com/Anvil) made their first contribution in [#&#8203;8567](pydantic/pydantic#8567)
-   [@&#8203;hungtsetse](https://github.com/hungtsetse) made their first contribution in [#&#8203;8546](pydantic/pydantic#8546)
-   [@&#8203;StrawHatDrag0n](https://github.com/StrawHatDrag0n) made their first contribution in [#&#8203;8583](pydantic/pydantic#8583)

##### `pydantic-core`

-   [@&#8203;mariuswinger](https://github.com/mariuswinger) made their first contribution in [pydantic/pydantic-core#1087](pydantic/pydantic-core#1087)
-   [@&#8203;adamchainz](https://github.com/adamchainz) made their first contribution in [pydantic/pydantic-core#1090](pydantic/pydantic-core#1090)
-   [@&#8203;akx](https://github.com/akx) made their first contribution in [pydantic/pydantic-core#1123](pydantic/pydantic-core#1123)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4xMzAuMCIsInVwZGF0ZWRJblZlciI6IjM3LjEzMC4wIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIn0=-->

Reviewed-on: https://git.apud.pl/jacek/adventofcode/pulls/57
Co-authored-by: Renovate <renovate@apud.pl>
Co-committed-by: Renovate <renovate@apud.pl>
@Viicos
Copy link
Contributor Author

Viicos commented Feb 24, 2024

@samuelcolvin @sydney-runkle

I created flake8-pydantic yesterday, with a bunch of handy rules. One of them hopefully covers most of the use cases we encountered.

Let me know if you have other rules in mind that could be implemented!

@sydney-runkle
Copy link
Member

@Viicos,

Nice! That's great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting author revision relnotes-change Used for changes to existing functionality which don't have a better categorization.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants