mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	decorator: Refactor decorators expecting UserProfile with ParamSpec.
Decorators like `require_server_admin_api` turns user_profile into a positional-only parameter, requiring the callers to stop passing it as a keyword argument. Functions like `get_chart_data` that gets decorated by both `require_non_guest_user` and `has_request_variables` now have accurate type annotation during type checking, with the first two parameters turned into positional-only, and thus the change in `analytics.views.stats`. Signed-off-by: Zixuan James Li <p359101898@gmail.com>
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							adae8b6d42
						
					
				
				
					commit
					ca0d2f6854
				
			@@ -131,7 +131,7 @@ def get_chart_data_for_realm(
 | 
				
			|||||||
    except Realm.DoesNotExist:
 | 
					    except Realm.DoesNotExist:
 | 
				
			||||||
        raise JsonableError(_("Invalid organization"))
 | 
					        raise JsonableError(_("Invalid organization"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return get_chart_data(request, user_profile=user_profile, realm=realm, **kwargs)
 | 
					    return get_chart_data(request, user_profile, realm=realm, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@require_server_admin_api
 | 
					@require_server_admin_api
 | 
				
			||||||
@@ -148,7 +148,7 @@ def get_chart_data_for_remote_realm(
 | 
				
			|||||||
    server = RemoteZulipServer.objects.get(id=remote_server_id)
 | 
					    server = RemoteZulipServer.objects.get(id=remote_server_id)
 | 
				
			||||||
    return get_chart_data(
 | 
					    return get_chart_data(
 | 
				
			||||||
        request,
 | 
					        request,
 | 
				
			||||||
        user_profile=user_profile,
 | 
					        user_profile,
 | 
				
			||||||
        server=server,
 | 
					        server=server,
 | 
				
			||||||
        remote=True,
 | 
					        remote=True,
 | 
				
			||||||
        remote_realm_id=int(remote_realm_id),
 | 
					        remote_realm_id=int(remote_realm_id),
 | 
				
			||||||
@@ -179,7 +179,7 @@ def stats_for_remote_installation(request: HttpRequest, remote_server_id: int) -
 | 
				
			|||||||
def get_chart_data_for_installation(
 | 
					def get_chart_data_for_installation(
 | 
				
			||||||
    request: HttpRequest, /, user_profile: UserProfile, chart_name: str = REQ(), **kwargs: Any
 | 
					    request: HttpRequest, /, user_profile: UserProfile, chart_name: str = REQ(), **kwargs: Any
 | 
				
			||||||
) -> HttpResponse:
 | 
					) -> HttpResponse:
 | 
				
			||||||
    return get_chart_data(request, user_profile=user_profile, for_installation=True, **kwargs)
 | 
					    return get_chart_data(request, user_profile, for_installation=True, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@require_server_admin_api
 | 
					@require_server_admin_api
 | 
				
			||||||
@@ -196,7 +196,7 @@ def get_chart_data_for_remote_installation(
 | 
				
			|||||||
    server = RemoteZulipServer.objects.get(id=remote_server_id)
 | 
					    server = RemoteZulipServer.objects.get(id=remote_server_id)
 | 
				
			||||||
    return get_chart_data(
 | 
					    return get_chart_data(
 | 
				
			||||||
        request,
 | 
					        request,
 | 
				
			||||||
        user_profile=user_profile,
 | 
					        user_profile,
 | 
				
			||||||
        for_installation=True,
 | 
					        for_installation=True,
 | 
				
			||||||
        remote=True,
 | 
					        remote=True,
 | 
				
			||||||
        server=server,
 | 
					        server=server,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -142,52 +142,76 @@ def require_post(
 | 
				
			|||||||
    return wrapper
 | 
					    return wrapper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def require_realm_owner(func: ViewFuncT) -> ViewFuncT:
 | 
					def require_realm_owner(
 | 
				
			||||||
 | 
					    func: Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]
 | 
				
			||||||
 | 
					) -> Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]:
 | 
				
			||||||
    @wraps(func)
 | 
					    @wraps(func)
 | 
				
			||||||
    def wrapper(
 | 
					    def wrapper(
 | 
				
			||||||
        request: HttpRequest, user_profile: UserProfile, *args: object, **kwargs: object
 | 
					        request: HttpRequest,
 | 
				
			||||||
 | 
					        user_profile: UserProfile,
 | 
				
			||||||
 | 
					        /,
 | 
				
			||||||
 | 
					        *args: ParamT.args,
 | 
				
			||||||
 | 
					        **kwargs: ParamT.kwargs,
 | 
				
			||||||
    ) -> HttpResponse:
 | 
					    ) -> HttpResponse:
 | 
				
			||||||
        if not user_profile.is_realm_owner:
 | 
					        if not user_profile.is_realm_owner:
 | 
				
			||||||
            raise OrganizationOwnerRequired()
 | 
					            raise OrganizationOwnerRequired()
 | 
				
			||||||
        return func(request, user_profile, *args, **kwargs)
 | 
					        return func(request, user_profile, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return cast(ViewFuncT, wrapper)  # https://github.com/python/mypy/issues/1927
 | 
					    return wrapper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def require_realm_admin(func: ViewFuncT) -> ViewFuncT:
 | 
					def require_realm_admin(
 | 
				
			||||||
 | 
					    func: Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]
 | 
				
			||||||
 | 
					) -> Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]:
 | 
				
			||||||
    @wraps(func)
 | 
					    @wraps(func)
 | 
				
			||||||
    def wrapper(
 | 
					    def wrapper(
 | 
				
			||||||
        request: HttpRequest, user_profile: UserProfile, *args: object, **kwargs: object
 | 
					        request: HttpRequest,
 | 
				
			||||||
 | 
					        user_profile: UserProfile,
 | 
				
			||||||
 | 
					        /,
 | 
				
			||||||
 | 
					        *args: ParamT.args,
 | 
				
			||||||
 | 
					        **kwargs: ParamT.kwargs,
 | 
				
			||||||
    ) -> HttpResponse:
 | 
					    ) -> HttpResponse:
 | 
				
			||||||
        if not user_profile.is_realm_admin:
 | 
					        if not user_profile.is_realm_admin:
 | 
				
			||||||
            raise OrganizationAdministratorRequired()
 | 
					            raise OrganizationAdministratorRequired()
 | 
				
			||||||
        return func(request, user_profile, *args, **kwargs)
 | 
					        return func(request, user_profile, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return cast(ViewFuncT, wrapper)  # https://github.com/python/mypy/issues/1927
 | 
					    return wrapper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def require_organization_member(func: ViewFuncT) -> ViewFuncT:
 | 
					def require_organization_member(
 | 
				
			||||||
 | 
					    func: Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]
 | 
				
			||||||
 | 
					) -> Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]:
 | 
				
			||||||
    @wraps(func)
 | 
					    @wraps(func)
 | 
				
			||||||
    def wrapper(
 | 
					    def wrapper(
 | 
				
			||||||
        request: HttpRequest, user_profile: UserProfile, *args: object, **kwargs: object
 | 
					        request: HttpRequest,
 | 
				
			||||||
 | 
					        user_profile: UserProfile,
 | 
				
			||||||
 | 
					        /,
 | 
				
			||||||
 | 
					        *args: ParamT.args,
 | 
				
			||||||
 | 
					        **kwargs: ParamT.kwargs,
 | 
				
			||||||
    ) -> HttpResponse:
 | 
					    ) -> HttpResponse:
 | 
				
			||||||
        if user_profile.role > UserProfile.ROLE_MEMBER:
 | 
					        if user_profile.role > UserProfile.ROLE_MEMBER:
 | 
				
			||||||
            raise OrganizationMemberRequired()
 | 
					            raise OrganizationMemberRequired()
 | 
				
			||||||
        return func(request, user_profile, *args, **kwargs)
 | 
					        return func(request, user_profile, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return cast(ViewFuncT, wrapper)  # https://github.com/python/mypy/issues/1927
 | 
					    return wrapper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def require_billing_access(func: ViewFuncT) -> ViewFuncT:
 | 
					def require_billing_access(
 | 
				
			||||||
 | 
					    func: Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]
 | 
				
			||||||
 | 
					) -> Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]:
 | 
				
			||||||
    @wraps(func)
 | 
					    @wraps(func)
 | 
				
			||||||
    def wrapper(
 | 
					    def wrapper(
 | 
				
			||||||
        request: HttpRequest, user_profile: UserProfile, *args: object, **kwargs: object
 | 
					        request: HttpRequest,
 | 
				
			||||||
 | 
					        user_profile: UserProfile,
 | 
				
			||||||
 | 
					        /,
 | 
				
			||||||
 | 
					        *args: ParamT.args,
 | 
				
			||||||
 | 
					        **kwargs: ParamT.kwargs,
 | 
				
			||||||
    ) -> HttpResponse:
 | 
					    ) -> HttpResponse:
 | 
				
			||||||
        if not user_profile.has_billing_access:
 | 
					        if not user_profile.has_billing_access:
 | 
				
			||||||
            raise JsonableError(_("Must be a billing administrator or an organization owner"))
 | 
					            raise JsonableError(_("Must be a billing administrator or an organization owner"))
 | 
				
			||||||
        return func(request, user_profile, *args, **kwargs)
 | 
					        return func(request, user_profile, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return cast(ViewFuncT, wrapper)  # https://github.com/python/mypy/issues/1927
 | 
					    return wrapper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def process_client(
 | 
					def process_client(
 | 
				
			||||||
@@ -627,22 +651,34 @@ def require_server_admin_api(
 | 
				
			|||||||
    return _wrapped_view_func
 | 
					    return _wrapped_view_func
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def require_non_guest_user(view_func: ViewFuncT) -> ViewFuncT:
 | 
					def require_non_guest_user(
 | 
				
			||||||
 | 
					    view_func: Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]
 | 
				
			||||||
 | 
					) -> Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]:
 | 
				
			||||||
    @wraps(view_func)
 | 
					    @wraps(view_func)
 | 
				
			||||||
    def _wrapped_view_func(
 | 
					    def _wrapped_view_func(
 | 
				
			||||||
        request: HttpRequest, user_profile: UserProfile, *args: object, **kwargs: object
 | 
					        request: HttpRequest,
 | 
				
			||||||
 | 
					        user_profile: UserProfile,
 | 
				
			||||||
 | 
					        /,
 | 
				
			||||||
 | 
					        *args: ParamT.args,
 | 
				
			||||||
 | 
					        **kwargs: ParamT.kwargs,
 | 
				
			||||||
    ) -> HttpResponse:
 | 
					    ) -> HttpResponse:
 | 
				
			||||||
        if user_profile.is_guest:
 | 
					        if user_profile.is_guest:
 | 
				
			||||||
            raise JsonableError(_("Not allowed for guest users"))
 | 
					            raise JsonableError(_("Not allowed for guest users"))
 | 
				
			||||||
        return view_func(request, user_profile, *args, **kwargs)
 | 
					        return view_func(request, user_profile, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return cast(ViewFuncT, _wrapped_view_func)  # https://github.com/python/mypy/issues/1927
 | 
					    return _wrapped_view_func
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def require_member_or_admin(view_func: ViewFuncT) -> ViewFuncT:
 | 
					def require_member_or_admin(
 | 
				
			||||||
 | 
					    view_func: Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]
 | 
				
			||||||
 | 
					) -> Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]:
 | 
				
			||||||
    @wraps(view_func)
 | 
					    @wraps(view_func)
 | 
				
			||||||
    def _wrapped_view_func(
 | 
					    def _wrapped_view_func(
 | 
				
			||||||
        request: HttpRequest, user_profile: UserProfile, *args: object, **kwargs: object
 | 
					        request: HttpRequest,
 | 
				
			||||||
 | 
					        user_profile: UserProfile,
 | 
				
			||||||
 | 
					        /,
 | 
				
			||||||
 | 
					        *args: ParamT.args,
 | 
				
			||||||
 | 
					        **kwargs: ParamT.kwargs,
 | 
				
			||||||
    ) -> HttpResponse:
 | 
					    ) -> HttpResponse:
 | 
				
			||||||
        if user_profile.is_guest:
 | 
					        if user_profile.is_guest:
 | 
				
			||||||
            raise JsonableError(_("Not allowed for guest users"))
 | 
					            raise JsonableError(_("Not allowed for guest users"))
 | 
				
			||||||
@@ -650,20 +686,26 @@ def require_member_or_admin(view_func: ViewFuncT) -> ViewFuncT:
 | 
				
			|||||||
            raise JsonableError(_("This endpoint does not accept bot requests."))
 | 
					            raise JsonableError(_("This endpoint does not accept bot requests."))
 | 
				
			||||||
        return view_func(request, user_profile, *args, **kwargs)
 | 
					        return view_func(request, user_profile, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return cast(ViewFuncT, _wrapped_view_func)  # https://github.com/python/mypy/issues/1927
 | 
					    return _wrapped_view_func
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def require_user_group_edit_permission(view_func: ViewFuncT) -> ViewFuncT:
 | 
					def require_user_group_edit_permission(
 | 
				
			||||||
 | 
					    view_func: Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]
 | 
				
			||||||
 | 
					) -> Callable[Concatenate[HttpRequest, UserProfile, ParamT], HttpResponse]:
 | 
				
			||||||
    @require_member_or_admin
 | 
					    @require_member_or_admin
 | 
				
			||||||
    @wraps(view_func)
 | 
					    @wraps(view_func)
 | 
				
			||||||
    def _wrapped_view_func(
 | 
					    def _wrapped_view_func(
 | 
				
			||||||
        request: HttpRequest, user_profile: UserProfile, *args: object, **kwargs: object
 | 
					        request: HttpRequest,
 | 
				
			||||||
 | 
					        user_profile: UserProfile,
 | 
				
			||||||
 | 
					        /,
 | 
				
			||||||
 | 
					        *args: ParamT.args,
 | 
				
			||||||
 | 
					        **kwargs: ParamT.kwargs,
 | 
				
			||||||
    ) -> HttpResponse:
 | 
					    ) -> HttpResponse:
 | 
				
			||||||
        if not user_profile.can_edit_user_groups():
 | 
					        if not user_profile.can_edit_user_groups():
 | 
				
			||||||
            raise JsonableError(_("Insufficient permission"))
 | 
					            raise JsonableError(_("Insufficient permission"))
 | 
				
			||||||
        return view_func(request, user_profile, *args, **kwargs)
 | 
					        return view_func(request, user_profile, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return cast(ViewFuncT, _wrapped_view_func)  # https://github.com/python/mypy/issues/1927
 | 
					    return _wrapped_view_func
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# This API endpoint is used only for the mobile apps.  It is part of a
 | 
					# This API endpoint is used only for the mobile apps.  It is part of a
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user