diff --git a/easy/helper.py b/easy/helper.py index e4c5487..38dbc65 100644 --- a/easy/helper.py +++ b/easy/helper.py @@ -1,9 +1,14 @@ +from __future__ import annotations +from typing import Callable, Union, Any + import django EASY_CACHE_TEMPLATE_METHOD = 'easy.{}.{}.{}' EASY_CACHE_TEMPLATE_OBJ = 'easy.{}.{}' +Model: django.db.models.Model + class Nothing(object): def __str__(self): return 'Error' @@ -12,17 +17,39 @@ def __unicode__(self): return u'Error' -def deep_getattribute(obj, attr): +def deep_getattribute(obj: object, attr: str) -> object: + """ + Retrieves the value of a nested attribute from an object. + + Args: + obj (object): The object to retrieve the attribute from. + attr (str): The attribute to retrieve, in the form of a dot-separated string. + + Returns: + object: The value of the attribute, or a Nothing instance if the attribute does not exist. + """ attrs = attr.split(".") for i in attrs: obj = getattr(obj, i, Nothing()) return obj -def get_django_filter(django_filter, load='django'): +def get_django_filter(django_filter: str, load: str = 'django') -> Callable: + """ + Retrieves a Django filter method from the specified templatetag library. + Args: + django_filter (str): The name of the Django filter method. + load (str, optional): The name of the templatetag library to load. Defaults to 'django'. + + Returns: + Callable: The Django filter method. + + Raises: + Exception: If the specified Django filter or templatetag library does not exist. + """ if django.VERSION < (1, 9): from django.template.base import get_library - if load and not load == 'django': + if load and load != 'django': library = get_library(load) else: library_path = 'django.template.defaultfilters' @@ -37,30 +64,41 @@ def get_django_filter(django_filter, load='django'): from django.template.backends.django import get_installed_libraries from django.template.library import import_library libraries = get_installed_libraries() - if load and not load == 'django': + if load and load != 'django': library_path = libraries.get(load) if not library_path: - raise Exception('templatetag "{}" is not registered'.format(load)) + raise Exception(f'templatetag "{load}" is not registered') else: library_path = 'django.template.defaultfilters' library = import_library(library_path) filter_method = library.filters.get(django_filter) if not filter_method: - raise Exception('filter "{}" not exist on {} templatetag package'.format( - django_filter, load - )) + raise Exception(f'filter "{django_filter}" not exist on {load} templatetag package') return filter_method -def call_or_get(obj, attr, default=None): +def call_or_get(obj: object, attr: Union[str, Callable[[object], Any]], default: Any = None) -> Any: + """ + Calls the given attribute if it is a callable, otherwise retrieves its value. + + Args: + obj (object): The object to call the attribute on. + attr (Union[str, Callable[[object], Any]]): The attribute to call or retrieve. + default (Any, optional): The default value to return if the attribute's value is None or an instance of Nothing. + + Returns: + Any: The result of calling the attribute if it is a callable, otherwise its value. If the attribute's value is None or an instance of Nothing, returns the default value if it is provided, otherwise returns None. + """ ret = Nothing() - if hasattr(attr, '__call__'): + + if callable(attr): ret = attr(obj) + if isinstance(ret, Nothing): value = deep_getattribute(obj, attr) - if hasattr(value, '__call__'): + if callable(value): ret = value() else: ret = value @@ -71,14 +109,33 @@ def call_or_get(obj, attr, default=None): return ret -def get_model_name(model): +def get_model_name(model: Model) -> str: + """ + Retrieves the name of a Django model. + + Args: + model (Model): The Django model object. + + Returns: + str: The name of the model. + """ if django.VERSION < (1, 6): return model._meta.module_name else: return model._meta.model_name -def cache_method_key(model, method_name): +def cache_method_key(model: Model, method_name: str) -> str: + """ + Generates a cache key for a method of a model instance. + + Args: + model (Model): The model instance. + method_name (str): The name of the method. + + Returns: + str: The cache key. + """ return EASY_CACHE_TEMPLATE_METHOD.format( model._meta.app_label, get_model_name(model), @@ -87,7 +144,16 @@ def cache_method_key(model, method_name): ) -def cache_object_key(model): +def cache_object_key(model: Model) -> str: + """ + Generates a cache key for a model instance. + + Args: + model (Model): The model instance. + + Returns: + str: The cache key. + """ return EASY_CACHE_TEMPLATE_OBJ.format( model._meta.app_label, get_model_name(model), diff --git a/easy/util.py b/easy/util.py index e7bc829..7df07b7 100644 --- a/easy/util.py +++ b/easy/util.py @@ -1,11 +1,30 @@ +from typing import Optional + from django.shortcuts import redirect from django.contrib import messages -def action_response(request, message=None, level=messages.INFO, keep_querystring=True): - redirect_url = '.' +def action_response( + request: "HttpRequest", + message: Optional[str] = None, + level: int = messages.INFO, + keep_querystring: bool = True, +) -> "HttpResponseRedirect": + """ + Redirects the user to the current page with an optional message. + + Args: + request: The current request. + message: The message to display to the user. + level: The level of the message. + keep_querystring: Whether to keep the query string in the redirect URL. + + Returns: + An HttpResponseRedirect object. + """ + redirect_url = "." if keep_querystring and request.GET: - redirect_url = './?' + request.GET.urlencode() + redirect_url = "./?" + request.GET.urlencode() if message: messages.add_message(request, level, message, fail_silently=True) return redirect(redirect_url)