From a095fcd20f6d7b839c68704d7e13feb0688da079 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 1 Oct 2022 22:57:19 +0200 Subject: [PATCH 1/3] from __future__ import annotations --- waffle/admin.py | 8 +++++--- waffle/models.py | 36 +++++++++++++++++++----------------- waffle/views.py | 6 ++++-- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/waffle/admin.py b/waffle/admin.py index 57fc670a..1ec9592d 100755 --- a/waffle/admin.py +++ b/waffle/admin.py @@ -1,4 +1,6 @@ -from typing import Any, Dict, Tuple +from __future__ import annotations + +from typing import Any from django.contrib import admin from django.contrib.admin.models import LogEntry, CHANGE, DELETION @@ -15,7 +17,7 @@ class BaseAdmin(admin.ModelAdmin): search_fields = ('name', 'note') - def get_actions(self, request: HttpRequest) -> Dict[str, Any]: + def get_actions(self, request: HttpRequest) -> dict[str, Any]: actions = super().get_actions(request) if 'delete_selected' in actions: del actions['delete_selected'] @@ -73,7 +75,7 @@ class InformativeManyToManyRawIdWidget(ManyToManyRawIdWidget): Will display the names of the users in a parenthesised list after the input field. This widget works with all models that have a "name" field. """ - def label_and_url_for_value(self, values: Any) -> Tuple[str, str]: + def label_and_url_for_value(self, values: Any) -> tuple[str, str]: names = [] key = self.rel.get_related_field().name for value in values: diff --git a/waffle/models.py b/waffle/models.py index 019f54c9..3a5fa76d 100644 --- a/waffle/models.py +++ b/waffle/models.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import logging import random from decimal import Decimal -from typing import Any, Dict, List, Optional, Set, Tuple, Type, TypeVar +from typing import Any, TypeVar from django.conf import settings from django.contrib.auth.models import AbstractBaseUser, Group @@ -36,7 +38,7 @@ class Meta: def __str__(self) -> str: return self.name - def natural_key(self) -> Tuple[str]: + def natural_key(self) -> tuple[str]: return (self.name,) @classmethod @@ -44,7 +46,7 @@ def _cache_key(cls, name: str) -> str: return keyfmt(get_setting(cls.SINGLE_CACHE_KEY), name) @classmethod - def get(cls: Type[_BaseModelType], name: str) -> _BaseModelType: + def get(cls: type[_BaseModelType], name: str) -> _BaseModelType: cache = get_cache() cache_key = cls._cache_key(name) cached = cache.get(cache_key) @@ -63,14 +65,14 @@ def get(cls: Type[_BaseModelType], name: str) -> _BaseModelType: return obj @classmethod - def get_from_db(cls: Type[_BaseModelType], name: str) -> _BaseModelType: + def get_from_db(cls: type[_BaseModelType], name: str) -> _BaseModelType: objects = cls.objects if get_setting('READ_FROM_WRITE_DB'): objects = objects.using(router.db_for_write(cls)) return objects.get(name=name) @classmethod - def get_all(cls: Type[_BaseModelType]) -> List[_BaseModelType]: + def get_all(cls: type[_BaseModelType]) -> list[_BaseModelType]: cache = get_cache() cache_key = get_setting(cls.ALL_CACHE_KEY) cached = cache.get(cache_key) @@ -88,7 +90,7 @@ def get_all(cls: Type[_BaseModelType]) -> List[_BaseModelType]: return objs @classmethod - def get_all_from_db(cls: Type[_BaseModelType]) -> List[_BaseModelType]: + def get_all_from_db(cls: type[_BaseModelType]) -> list[_BaseModelType]: objects = cls.objects if get_setting('READ_FROM_WRITE_DB'): objects = objects.using(router.db_for_write(cls)) @@ -111,7 +113,7 @@ def save(self, *args: Any, **kwargs: Any) -> None: self.flush() return ret - def delete(self, *args: Any, **kwargs: Any) -> Tuple[int, Dict[str, int]]: + def delete(self, *args: Any, **kwargs: Any) -> tuple[int, dict[str, int]]: ret = super().delete(*args, **kwargs) if hasattr(transaction, 'on_commit'): transaction.on_commit(self.flush) @@ -120,7 +122,7 @@ def delete(self, *args: Any, **kwargs: Any) -> Tuple[int, Dict[str, int]]: return ret -def set_flag(request: HttpRequest, flag_name: str, active: Optional[bool] = True, session_only: bool = False) -> None: +def set_flag(request: HttpRequest, flag_name: str, active: bool | None = True, session_only: bool = False) -> None: """Set a flag value on a request object.""" if not hasattr(request, 'waffles'): request.waffles = {} @@ -219,7 +221,7 @@ def flush(self) -> None: keys = self.get_flush_keys() cache.delete_many(keys) - def get_flush_keys(self, flush_keys: Optional[List[str]] = None) -> List[str]: + def get_flush_keys(self, flush_keys: list[str] | None = None) -> list[str]: flush_keys = flush_keys or [] flush_keys.extend([ self._cache_key(self.name), @@ -227,7 +229,7 @@ def get_flush_keys(self, flush_keys: Optional[List[str]] = None) -> List[str]: ]) return flush_keys - def is_active_for_user(self, user: AbstractBaseUser) -> Optional[bool]: + def is_active_for_user(self, user: AbstractBaseUser) -> bool | None: if self.authenticated and user.is_authenticated: return True @@ -239,13 +241,13 @@ def is_active_for_user(self, user: AbstractBaseUser) -> Optional[bool]: return None - def _is_active_for_user(self, request: HttpRequest) -> Optional[bool]: + def _is_active_for_user(self, request: HttpRequest) -> bool | None: user = getattr(request, "user", None) if user: return self.is_active_for_user(user) return False - def _is_active_for_language(self, request: HttpRequest) -> Optional[bool]: + def _is_active_for_language(self, request: HttpRequest) -> bool | None: if self.languages: languages = [ln.strip() for ln in self.languages.split(',')] if (hasattr(request, 'LANGUAGE_CODE') and @@ -253,7 +255,7 @@ def _is_active_for_language(self, request: HttpRequest) -> Optional[bool]: return True return None - def is_active(self, request: HttpRequest, read_only: bool = False) -> Optional[bool]: + def is_active(self, request: HttpRequest, read_only: bool = False) -> bool | None: if not self.pk: log_level = get_setting('LOG_MISSING_FLAGS') if log_level: @@ -347,7 +349,7 @@ class Meta(AbstractBaseFlag.Meta): verbose_name = _('Flag') verbose_name_plural = _('Flags') - def get_flush_keys(self, flush_keys: Optional[List[str]] = None) -> List[str]: + def get_flush_keys(self, flush_keys: list[str] | None = None) -> list[str]: flush_keys = super().get_flush_keys(flush_keys) flush_keys.extend([ keyfmt(get_setting('FLAG_USERS_CACHE_KEY'), self.name), @@ -355,7 +357,7 @@ def get_flush_keys(self, flush_keys: Optional[List[str]] = None) -> List[str]: ]) return flush_keys - def _get_user_ids(self) -> Set[Any]: + def _get_user_ids(self) -> set[Any]: cache = get_cache() cache_key = keyfmt(get_setting('FLAG_USERS_CACHE_KEY'), self.name) cached = cache.get(cache_key) @@ -372,7 +374,7 @@ def _get_user_ids(self) -> Set[Any]: cache.add(cache_key, user_ids) return user_ids - def _get_group_ids(self) -> Set[Any]: + def _get_group_ids(self) -> set[Any]: cache = get_cache() cache_key = keyfmt(get_setting('FLAG_GROUPS_CACHE_KEY'), self.name) cached = cache.get(cache_key) @@ -389,7 +391,7 @@ def _get_group_ids(self) -> Set[Any]: cache.add(cache_key, group_ids) return group_ids - def is_active_for_user(self, user: AbstractBaseUser) -> Optional[bool]: + def is_active_for_user(self, user: AbstractBaseUser) -> bool | None: is_active = super().is_active_for_user(user) if is_active: return is_active diff --git a/waffle/views.py b/waffle/views.py index 2605451f..30ee1370 100644 --- a/waffle/views.py +++ b/waffle/views.py @@ -1,4 +1,6 @@ -from typing import Any, Dict +from __future__ import annotations + +from typing import Any from django.http import HttpRequest, HttpResponse, JsonResponse from django.template import loader @@ -39,7 +41,7 @@ def waffle_json(request): return JsonResponse(_generate_waffle_json(request)) -def _generate_waffle_json(request: HttpRequest) -> Dict[str, Dict[str, Any]]: +def _generate_waffle_json(request: HttpRequest) -> dict[str, dict[str, Any]]: flags = get_waffle_flag_model().get_all() flag_values = { f.name: { From 11c7fd4e9d82a3f53411bbc58939353401b2c85b Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 1 Oct 2022 23:45:07 +0200 Subject: [PATCH 2/3] from __future__ import annotations --- waffle/__init__.py | 18 ++++++++++-------- waffle/decorators.py | 12 +++++++----- waffle/mixins.py | 9 +++++---- waffle/testutils.py | 8 ++++---- waffle/utils.py | 6 ++++-- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/waffle/__init__.py b/waffle/__init__.py index 16ca877d..8d48519e 100755 --- a/waffle/__init__.py +++ b/waffle/__init__.py @@ -1,4 +1,6 @@ -from typing import TYPE_CHECKING, Optional, Type, Union +from __future__ import annotations + +from typing import TYPE_CHECKING import django from django.core.exceptions import ImproperlyConfigured @@ -14,7 +16,7 @@ __version__ = '.'.join(map(str, VERSION)) -def flag_is_active(request: HttpRequest, flag_name: str, read_only: bool = False) -> Optional[bool]: +def flag_is_active(request: HttpRequest, flag_name: str, read_only: bool = False) -> bool | None: flag = get_waffle_flag_model().get(flag_name) return flag.is_active(request, read_only=read_only) @@ -29,21 +31,21 @@ def sample_is_active(sample_name: str) -> bool: return sample.is_active() -def get_waffle_flag_model() -> Type['AbstractBaseFlag']: +def get_waffle_flag_model() -> type[AbstractBaseFlag]: return get_waffle_model('FLAG_MODEL') -def get_waffle_switch_model() -> Type['AbstractBaseSwitch']: +def get_waffle_switch_model() -> type[AbstractBaseSwitch]: return get_waffle_model('SWITCH_MODEL') -def get_waffle_sample_model() -> Type['AbstractBaseSample']: +def get_waffle_sample_model() -> type[AbstractBaseSample]: return get_waffle_model('SAMPLE_MODEL') -def get_waffle_model(setting_name: str) -> Union[ - Type['AbstractBaseFlag'], Type['AbstractBaseSwitch'], Type['AbstractBaseSample'] -]: +def get_waffle_model(setting_name: str) -> ( + type[AbstractBaseFlag] | type[AbstractBaseSwitch] | type[AbstractBaseSample] +): """ Returns the waffle Flag model that is active in this project. """ diff --git a/waffle/decorators.py b/waffle/decorators.py index 764a0401..664dd52a 100644 --- a/waffle/decorators.py +++ b/waffle/decorators.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from functools import wraps, WRAPPER_ASSIGNMENTS -from typing import Any, Callable, Optional, Union +from typing import Any, Callable from django.http import Http404, HttpRequest, HttpResponse, HttpResponsePermanentRedirect, HttpResponseRedirect from django.shortcuts import redirect @@ -9,7 +11,7 @@ def waffle_flag( - flag_name: str, redirect_to: Optional[Union[Callable, str]] = None, + flag_name: str, redirect_to: Callable | str | None = None, ) -> Callable[[Callable[[HttpRequest], HttpResponse]], Callable[[HttpRequest], HttpResponse]]: def decorator(view: Callable[[HttpRequest], HttpResponse]) -> Callable[[HttpRequest], HttpResponse]: @wraps(view, assigned=WRAPPER_ASSIGNMENTS) @@ -32,7 +34,7 @@ def _wrapped_view(request, *args, **kwargs): def waffle_switch( - switch_name: str, redirect_to: Optional[Union[Callable, str]] = None, + switch_name: str, redirect_to: Callable | str | None = None, ) -> Callable[[Callable[[HttpRequest], HttpResponse]], Callable[[HttpRequest], HttpResponse]]: def decorator(view: Callable[[HttpRequest], HttpResponse]) -> Callable[[HttpRequest], HttpResponse]: @wraps(view, assigned=WRAPPER_ASSIGNMENTS) @@ -55,8 +57,8 @@ def _wrapped_view(request, *args, **kwargs): def get_response_to_redirect( - view: Optional[Union[Callable, str]], *args: Any, **kwargs: Any, -) -> Optional[Union[HttpResponseRedirect, HttpResponsePermanentRedirect]]: + view: Callable | str | None, *args: Any, **kwargs: Any, +) -> HttpResponseRedirect | HttpResponsePermanentRedirect | None: try: return redirect(reverse(view, args=args, kwargs=kwargs)) if view else None except NoReverseMatch: diff --git a/waffle/mixins.py b/waffle/mixins.py index a42ef7da..1896ca3e 100644 --- a/waffle/mixins.py +++ b/waffle/mixins.py @@ -1,5 +1,6 @@ +from __future__ import annotations + from functools import partial -from typing import Optional from django.http import Http404 @@ -25,7 +26,7 @@ class WaffleFlagMixin(BaseWaffleMixin): waffle_flag """ - waffle_flag: Optional[str] = None + waffle_flag: str | None = None def dispatch(self, request, *args, **kwargs): func = partial(flag_is_active, request) @@ -43,7 +44,7 @@ class WaffleSampleMixin(BaseWaffleMixin): waffle_sample. """ - waffle_sample: Optional[str] = None + waffle_sample: str | None = None def dispatch(self, request, *args, **kwargs): active = self.validate_waffle(self.waffle_sample, sample_is_active) @@ -60,7 +61,7 @@ class WaffleSwitchMixin(BaseWaffleMixin): waffle_switch. """ - waffle_switch: Optional[str] = None + waffle_switch: str | None = None def dispatch(self, request, *args, **kwargs): active = self.validate_waffle(self.waffle_switch, switch_is_active) diff --git a/waffle/testutils.py b/waffle/testutils.py index c54b81cc..4519b28d 100644 --- a/waffle/testutils.py +++ b/waffle/testutils.py @@ -81,13 +81,13 @@ def get_value(self) -> bool: class override_flag(_overrider[Optional[bool]]): cls = get_waffle_flag_model() - def update(self, active: Optional[bool]) -> None: + def update(self, active: bool | None) -> None: obj = self.cls.objects.get(pk=self.obj.pk) obj.everyone = active obj.save() obj.flush() - def get_value(self) -> Optional[bool]: + def get_value(self) -> bool | None: return self.obj.everyone @@ -102,7 +102,7 @@ def get(self) -> None: self.obj = self.cls.objects.create(name=self.name, percent='0.0') self.created = True - def update(self, active: Union[bool, float]) -> None: + def update(self, active: bool | float) -> None: if active is True: p = 100.0 elif active is False: @@ -114,7 +114,7 @@ def update(self, active: Union[bool, float]) -> None: obj.save() obj.flush() - def get_value(self) -> Union[bool, float]: + def get_value(self) -> bool | float: p = self.obj.percent if p == 100.0: return True diff --git a/waffle/utils.py b/waffle/utils.py index 6ec976f5..9e709420 100644 --- a/waffle/utils.py +++ b/waffle/utils.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import hashlib -from typing import Any, Optional +from typing import Any from django.conf import settings from django.core.cache import BaseCache, caches @@ -15,7 +17,7 @@ def get_setting(name: str, default: Any = None) -> Any: return getattr(defaults, name, default) -def keyfmt(k: str, v: Optional[str] = None) -> str: +def keyfmt(k: str, v: str | None = None) -> str: prefix = get_setting('CACHE_PREFIX') + waffle.__version__ if v is None: key = prefix + k From 40e20372a3a70cc8074ae4e1e015923c5e742039 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 1 Oct 2022 23:50:32 +0200 Subject: [PATCH 3/3] from __future__ import annotations --- waffle/testutils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/waffle/testutils.py b/waffle/testutils.py index 4519b28d..c54b81cc 100644 --- a/waffle/testutils.py +++ b/waffle/testutils.py @@ -81,13 +81,13 @@ def get_value(self) -> bool: class override_flag(_overrider[Optional[bool]]): cls = get_waffle_flag_model() - def update(self, active: bool | None) -> None: + def update(self, active: Optional[bool]) -> None: obj = self.cls.objects.get(pk=self.obj.pk) obj.everyone = active obj.save() obj.flush() - def get_value(self) -> bool | None: + def get_value(self) -> Optional[bool]: return self.obj.everyone @@ -102,7 +102,7 @@ def get(self) -> None: self.obj = self.cls.objects.create(name=self.name, percent='0.0') self.created = True - def update(self, active: bool | float) -> None: + def update(self, active: Union[bool, float]) -> None: if active is True: p = 100.0 elif active is False: @@ -114,7 +114,7 @@ def update(self, active: bool | float) -> None: obj.save() obj.flush() - def get_value(self) -> bool | float: + def get_value(self) -> Union[bool, float]: p = self.obj.percent if p == 100.0: return True