From 6fe7f45725dd0fc94fb3ce7ab3b3ff477b10401a Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 13 May 2021 11:33:01 -0700 Subject: [PATCH] inform mypy that g has arbitrary attributes --- CHANGES.rst | 2 ++ src/flask/ctx.py | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index dbcc0667..2214dc49 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,8 @@ Unreleased is deprecated. :pr:`4019` - Mark top-level names as exported so type checking understands imports in user projects. :issue:`4024` +- Fix type annotation for ``g`` and inform mypy that it is a namespace + object that has arbitrary attributes. :issue:`4020` Version 2.0.0 diff --git a/src/flask/ctx.py b/src/flask/ctx.py index 70de8cad..065edd5f 100644 --- a/src/flask/ctx.py +++ b/src/flask/ctx.py @@ -41,6 +41,24 @@ class _AppCtxGlobals: .. versionadded:: 0.10 """ + # Define attr methods to let mypy know this is a namespace object + # that has arbitrary attributes. + + def __getattr__(self, name: str) -> t.Any: + try: + return self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + + def __setattr__(self, name: str, value: t.Any) -> None: + self.__dict__[name] = value + + def __delattr__(self, name: str) -> None: + try: + del self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + def get(self, name: str, default: t.Optional[t.Any] = None) -> t.Any: """Get an attribute by name, or a default value. Like :meth:`dict.get`. @@ -78,10 +96,10 @@ class _AppCtxGlobals: """ return self.__dict__.setdefault(name, default) - def __contains__(self, item: t.Any) -> bool: + def __contains__(self, item: str) -> bool: return item in self.__dict__ - def __iter__(self) -> t.Iterator: + def __iter__(self) -> t.Iterator[str]: return iter(self.__dict__) def __repr__(self) -> str: