8000 Pyright Compatibility · openapi-generators openapi-python-client · Discussion #909 · GitHub
[go: up one dir, main page]

Skip to content

Pyright Compatibility #909

Closed
dbanty started this conversation in Feature request
Dec 15, 2023 · 3 comments · 1 reply
Discussion options

You must be logged in to vote

Replies: 3 comments 1 reply

Comment options

You must be logged in to vote
1 reply
@dbanty
Comment options

dbanty Jan 25, 2024
Maintainer Author

Comment options

Just chiming in to say that I very much would like to see this too. I default to using both Mypy and Pyright together, both in strict mode, and for the project I'm working on now, openapi-python-client using underscores for these attributes is the one thing that's preventing me from using Pyright. I'm contemplating overriding the template just to get around it.

One option here could be to use dataclass(frozen=True). That would make the type checker complain loudly if you ever attempted to change the attribute. For the caching of the client, it's not the prettiest solution in the world but I suppose something like a class attribute with a WeakKeyDictionary could be used as the cache:

# eq=False makes the dataclass use id-based hashing (inherits object.__hash__)
@dataclasses.dataclass(frozen=True, eq=False)
class Client:
    base_url: str

    _client_cache: ClassVar[
        MutableMapping["Client", httpx.Client]
    ] = weakref.WeakKeyDictionary()

    def set_httpx_client(self, client: httpx.Client) -> "Client":
        self._client_cache[self] = client
        return self

    def get_httpx_client(self) -> httpx.Client:
        client = self._client_cache.get(self)
        if client is None:
            client = httpx.Client(
                base_url=self.base_url,
            )
            self._client_cache[self] = client
        return client

This seems to work fine in testing, and PyCharm, Mypy and Pyright all correctly complain if you try to assign something to base_url. In theory the cache should be cleared automatically when the Client object owning the httpx.Client gets garbage collected.

Alternatively functools.cached_property also works (it writes directly to the instance's __dict__ so bypasses the frozen check in __setattr__), though I'm not sure how you'd implement set_httpx_client in that case. I think you'd have to write to __dict__ manually yourself.

Yet another option is to have the _client field be another object that in turn holds the httpx.Client, so that even though _client is frozen and can't be reassigned, the object itself is mutable:

class _ClientCacher:
    def __init__(self):
        self._client: httpx.Client | None = None

    def set(self, client: httpx.Client) -> None:
        self._client = client

    def get(self, parent: "Client") -> httpx.Client:
        client = self._client
        if client is None:
            client = httpx.Client(base_url=parent.base_url,)
            self._client = client
        return client


@dataclasses.dataclass(frozen=True, eq=False)
class Client:
    base_url: str

    _client: _ClientCacher = dataclasses.field(default_factory=_ClientCacher)

    def set_httpx_client(self, client: httpx.Client) -> "Client":
        self._client.set(client)
        return self

    def get_httpx_client(self) -> httpx.Client:
        return self._client.get(self)

attrs also allows you to selectively specify fields as frozen using on_setattr, but I'd be surprised if that worked nicely with type checkers.

Essentially it's perfectly doable, and it's up to personal preference which solution seems less bad. I find the WeakKeyDictionary variant the cleanest personally, but YMMV.

You must be logged in to vote
0 replies
Comment options

dbanty
Mar 27, 2024
Maintainer Author

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants
0