Skip to content

Comments

Use contextvars instead of patching pydantic internals.#44

Merged
dchukhin merged 1 commit intolincolnloop:mainfrom
apollo13:pydantic2-fixes
Oct 8, 2024
Merged

Use contextvars instead of patching pydantic internals.#44
dchukhin merged 1 commit intolincolnloop:mainfrom
apollo13:pydantic2-fixes

Conversation

@apollo13
Copy link
Contributor

@dchukhin as requested the new PR rebased on main. I'll leave a comment or two inline to explain a few things.

import sys
from io import StringIO
from types import GenericAlias
from typing import (
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most of these where unused, so I removed them, also see the comment on pydantic.fields below.

from typing import Any, cast, get_args

from pydantic import PrivateAttr
from pydantic._internal._config import config_keys
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This comment belongs to the pydantic.fields import one line below, but github doesn't allow me to set a comment there. I assume Field is ment to be here so from goodconf import Field works. Maybe an extra comment should point that out? Maybe even add an __all__?

from pydantic import PrivateAttr
from pydantic._internal._config import config_keys
from pydantic.fields import ( # noqa
Field,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I assume Field is ment to be here so from goodconf import Field works. Maybe an extra comment should point that out? Maybe even add an __all__?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not positive why the from goodconf import Field is there, but I imagine removing it will break something for someone. Both a comment and adding to __all__ sound good to me.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, the intent was being able ti import it from goodconf

return ""


CONFIG_CONTEXT = ContextVar("_goodconf_config")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This might need some consideration whether this is threadsafe or not. I think it might be since a single thread can only call load on one settings class concurrently.

def load(self, filename: str | None = None) -> None:
"""Find config file and set values"""
if filename:
values = _load_config(filename)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Even if we are not using my approach with context vars and keep the current approach it might make sense to simply set ._config_file here instead and leave the actual loading to our config source so everything is in one place.

g = G()
g.load()
mocked_load_config.assert_called_once_with(str(path))
assert g._config_file == str(path)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed and similar below because _config_file no longer exists and the assertion doesn't provide much value anyways since we check that load_config is called with that path.

Copy link
Contributor

@dchukhin dchukhin left a comment

Choose a reason for hiding this comment

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

The changes look reasonable to me.
Thanks @apollo13!
I don't have any concerns at the moment; could you make the tiny changes from my comment?

from pydantic import PrivateAttr
from pydantic._internal._config import config_keys
from pydantic.fields import ( # noqa
Field,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not positive why the from goodconf import Field is there, but I imagine removing it will break something for someone. Both a comment and adding to __all__ sound good to me.

return self.load()
if kwargs or load:
return self._load(config_file, kwargs)
else: # Patch `load` to properly pass on the filename
Copy link
Contributor

Choose a reason for hiding this comment

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

Non-critical comment: technically-speaking, the else is not needed here

@apollo13 apollo13 marked this pull request as draft August 30, 2024 17:06
@apollo13
Copy link
Contributor Author

I have another idea that I want to try that might work without contextvars.

@apollo13 apollo13 force-pushed the pydantic2-fixes branch 2 times, most recently from 43d1417 to bdc85f1 Compare August 30, 2024 18:02
@apollo13 apollo13 marked this pull request as ready for review August 30, 2024 18:05
"pydantic>=2.0",
"pydantic-settings>=2.0",
"pydantic>=2.7",
"pydantic-settings>=2.4",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Needed because accessing the current state requires pydantic-settings>=2.4 which in turn requires pydantic>=2.7 hence I raised that as well.

@apollo13 apollo13 requested a review from dchukhin August 30, 2024 18:06
@dchukhin dchukhin merged commit 6d9a49f into lincolnloop:main Oct 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants