fix: add type hints to whitelisted methods 3 (#37149)

* fix(apps): add type hints to whitelisted methods

* fix(recorder): add type hints to whitelisted methods

* fix(comments): add type hints to whitelisted methods

* fix(oauth2): add type hints to whitelisted methods

* fix(google_calendar): add type hints to whitelisted methods

* fix(print): add type hints to whitelisted methods

* fix(print_format_builder): add type hints to whitelisted methods

* refactor(network_printer_settings): remove unused args

* fix(document): add type hints to whitelisted methods

* fix(user_settings): add type hints to whitelisted methods

* fix(mapper): add type hints to whitelisted methods

* fix(connected_app): add type hints to whitelisted methods

* fix(google_contacts): add type hints to whitelisted methods

* fix(frappecloud_billing): add type hints to whitelisted methods

* test: rewrite test to fit the strict type check

* fix(social_login_key): add type hints to whitelisted methods

* fix(share): add type hints to whitelisted methods

* fix(webhook): add type hints to whitelisted methods

* fix(workflow): add type hints to whitelisted methods

* fix(workflow main): add type hints to whitelisted methods

* fix(workflow_action): add type hints to whitelisted methods

* fix: flexible type hint

* fix(client): add type hints to whitelisted methods

* fix: fix some of the tighter types

* fix(frappecloud_billing): add str typehint to whitelisted endpoint

* fix: target_doc can be dict/json string

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
This commit is contained in:
Aarol D'Souza 2026-02-20 12:20:19 +05:30 committed by GitHub
parent 101a7f40b6
commit c55ff193a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 136 additions and 83 deletions

View file

@ -88,7 +88,7 @@ def get_default_path():
@frappe.whitelist()
def set_app_as_default(app_name):
def set_app_as_default(app_name: str):
if frappe.db.get_value("User", frappe.session.user, "default_app") == app_name:
frappe.db.set_value("User", frappe.session.user, "default_app", "")
else:
@ -96,7 +96,7 @@ def set_app_as_default(app_name):
@frappe.whitelist()
def get_incomplete_setup_route(current_app, app_route):
def get_incomplete_setup_route(current_app: str, app_route: str):
pending_apps = get_apps_with_incomplete_dependencies(current_app)
if not pending_apps:

View file

@ -1,7 +1,7 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# License: MIT. See LICENSE
from datetime import timedelta
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
@ -560,8 +560,8 @@ def make_auto_repeat(
doctype: str,
docname: str | int,
frequency: str = "Daily",
start_date: str | None = None,
end_date: str | None = None,
start_date: str | datetime | None = None,
end_date: str | datetime | None = None,
):
if not start_date:
start_date = getdate(today())

View file

@ -2,7 +2,7 @@
# License: MIT. See LICENSE
import json
import os
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
import frappe
import frappe.model
@ -25,18 +25,18 @@ Requests via FrappeClient are also handled here.
@frappe.whitelist()
def get_list(
doctype,
fields=None,
filters=None,
group_by=None,
order_by=None,
limit_start=None,
limit_page_length=20,
parent=None,
debug: bool = False,
as_dict: bool = True,
or_filters=None,
expand=None,
doctype: str,
fields: str | list[str | dict[str, Any]] | None = None,
filters: str | list | dict[str, Any] | None = None,
group_by: str | list[str] | None = None,
order_by: str | list[str] | None = None,
limit_start: int | str | None = None,
limit_page_length: int | str = 20,
parent: str | None = None,
debug: bool | int = False,
as_dict: bool | int = True,
or_filters: str | list[list] | dict[str, Any] | None = None,
expand: str | list[str] | None = None,
):
"""Return a list of records by filters, fields, ordering and limit.
@ -76,7 +76,12 @@ def get_list(
@frappe.whitelist()
def get_count(doctype, filters=None, debug=False, cache=False):
def get_count(
doctype: str,
filters: str | list | dict[str, Any] | None = None,
debug: int | bool = False,
cache: int | bool = False,
):
from frappe.desk.reportview import get_count
frappe.form_dict.doctype = doctype
@ -87,7 +92,12 @@ def get_count(doctype, filters=None, debug=False, cache=False):
@frappe.whitelist()
def get(doctype, name=None, filters=None, parent=None):
def get(
doctype: str,
name: str | int | None = None,
filters: str | list | dict[str, Any] | None = None,
parent: str | None = None,
):
"""Return a document by name or filters.
:param doctype: DocType of the document to be returned
@ -108,7 +118,14 @@ def get(doctype, name=None, filters=None, parent=None):
@frappe.whitelist()
def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, parent=None):
def get_value(
doctype: str,
fieldname: str | list[str] | dict[str, Any],
filters: str | list | dict[str, Any] | None = None,
as_dict: int | bool = True,
debug: int | bool = False,
parent: str | None = None,
):
"""Return a value from a document.
:param doctype: DocType to be queried
@ -156,7 +173,7 @@ def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, paren
@frappe.whitelist()
def get_single_value(doctype, field):
def get_single_value(doctype: str, field: str):
if not frappe.has_permission(doctype):
frappe.throw(_("No permission for {0}").format(_(doctype)), frappe.PermissionError)
@ -164,7 +181,7 @@ def get_single_value(doctype, field):
@frappe.whitelist(methods=["POST", "PUT"])
def set_value(doctype, name, fieldname, value=None):
def set_value(doctype: str, name: str | int, fieldname: str | dict[str, Any], value: Any | None = None):
"""Set a value using get_doc, group of values
:param doctype: DocType of the document
@ -201,7 +218,7 @@ def set_value(doctype, name, fieldname, value=None):
@frappe.whitelist(methods=["POST", "PUT"])
def insert(doc=None):
def insert(doc: str | dict[str, Any] | None = None):
"""Insert a document
:param doc: JSON or dict object to be inserted"""
@ -212,7 +229,7 @@ def insert(doc=None):
@frappe.whitelist(methods=["POST", "PUT"])
def insert_many(docs=None):
def insert_many(docs: str | list[dict[str, Any]] | None = None):
"""Insert multiple documents
:param docs: JSON or list of dict objects to be inserted in one request"""
@ -226,7 +243,7 @@ def insert_many(docs=None):
@frappe.whitelist(methods=["POST", "PUT"])
def save(doc):
def save(doc: str | dict[str, Any]):
"""Update (save) an existing document
:param doc: JSON or dict object with the properties of the document to be updated"""
@ -240,7 +257,7 @@ def save(doc):
@frappe.whitelist(methods=["POST", "PUT"])
def rename_doc(doctype, old_name, new_name, merge=False):
def rename_doc(doctype: str, old_name: str | int, new_name: str | int, merge: bool = False):
"""Rename document
:param doctype: DocType of the document to be renamed
@ -251,7 +268,7 @@ def rename_doc(doctype, old_name, new_name, merge=False):
@frappe.whitelist(methods=["POST", "PUT"])
def submit(doc):
def submit(doc: str | dict[str, Any]):
"""Submit a document
:param doc: JSON or dict object to be submitted remotely"""
@ -265,7 +282,7 @@ def submit(doc):
@frappe.whitelist(methods=["POST", "PUT"])
def cancel(doctype, name):
def cancel(doctype: str, name: str | int):
"""Cancel a document
:param doctype: DocType of the document to be cancelled
@ -277,7 +294,7 @@ def cancel(doctype, name):
@frappe.whitelist(methods=["DELETE", "POST"])
def delete(doctype, name):
def delete(doctype: str, name: str | int):
"""Delete a remote document
:param doctype: DocType of the document to be deleted
@ -286,7 +303,7 @@ def delete(doctype, name):
@frappe.whitelist(methods=["POST", "PUT"])
def bulk_update(docs):
def bulk_update(docs: str):
"""Bulk update documents
:param docs: JSON list of documents to be updated remotely. Each document must have `docname` property"""
@ -305,7 +322,7 @@ def bulk_update(docs):
@frappe.whitelist()
def has_permission(doctype: str, docname: str, perm_type: str = "read"):
def has_permission(doctype: str, docname: str | int, perm_type: str = "read"):
"""Return a JSON with data whether the document has the requested permission.
:param doctype: DocType of the document to be checked
@ -316,7 +333,7 @@ def has_permission(doctype: str, docname: str, perm_type: str = "read"):
@frappe.whitelist()
def get_doc_permissions(doctype: str, docname: str):
def get_doc_permissions(doctype: str, docname: str | int):
"""Return an evaluated document permissions dict like `{"read":1, "write":1}`.
:param doctype: DocType of the document to be evaluated
@ -327,7 +344,7 @@ def get_doc_permissions(doctype: str, docname: str):
@frappe.whitelist()
def get_password(doctype: str, name: str, fieldname: str):
def get_password(doctype: str, name: str | int, fieldname: str):
"""Return a password type property. Only applicable for System Managers
:param doctype: DocType of the document that holds the password
@ -351,14 +368,14 @@ def get_time_zone():
@frappe.whitelist(methods=["POST", "PUT"])
def attach_file(
filename=None,
filedata=None,
doctype=None,
docname=None,
folder=None,
decode_base64=False,
is_private=None,
docfield=None,
filename: str | None = None,
filedata: str | None = None,
doctype: str | None = None,
docname: str | int | None = None,
folder: str | None = None,
decode_base64: int | bool = False,
is_private: int | bool | None = None,
docfield: str | None = None,
):
"""Attach a file to Document
@ -396,7 +413,7 @@ def attach_file(
@frappe.whitelist()
@http_cache(max_age=10 * 60)
def is_document_amended(doctype: str, docname: str):
def is_document_amended(doctype: str, docname: str | int):
if frappe.permissions.has_permission(doctype):
try:
return frappe.db.exists(doctype, {"amended_from": docname})
@ -409,7 +426,7 @@ def is_document_amended(doctype: str, docname: str):
@frappe.whitelist(methods=["GET", "POST"])
def validate_link_and_fetch(
doctype: str,
docname: str,
docname: str | int,
fields_to_fetch: list[str] | str | None = None,
# search_widget parameters
query: str | None = None,

View file

@ -88,7 +88,7 @@ class ConnectedApp(Document):
)
@frappe.whitelist()
def initiate_web_application_flow(self, user=None, success_uri=None):
def initiate_web_application_flow(self, user: str | None = None, success_uri: str | None = None):
"""Return an authorization URL for the user. Save state in Token Cache."""
user = user or frappe.session.user
oauth = self.get_oauth2_session(user, init=True)
@ -184,7 +184,7 @@ class ConnectedApp(Document):
@frappe.whitelist(methods=["GET"], allow_guest=True)
def callback(code=None, state=None):
def callback(code: str | None = None, state: str | None = None):
"""Handle client's code.
Called during the oauthorization flow by the remote oAuth2 server to
@ -223,7 +223,7 @@ def callback(code=None, state=None):
@frappe.whitelist()
def has_token(connected_app, connected_user=None):
def has_token(connected_app: str, connected_user: str | None = None):
app = frappe.get_doc("Connected App", connected_app)
token_cache = app.get_token_cache(connected_user or frappe.session.user)
return bool(token_cache and token_cache.get_password("access_token", False))

View file

@ -192,7 +192,7 @@ def get_authentication_url(client_id=None, redirect_uri=None):
@frappe.whitelist()
def google_callback(code=None):
def google_callback(code: str | None = None):
"""
Authorization code is sent to callback as per the API configuration
"""

View file

@ -49,7 +49,7 @@ class GoogleContacts(Document):
@frappe.whitelist(methods=["POST"])
def authorize_access(g_contact, reauthorize=False, code=None):
def authorize_access(g_contact: str, reauthorize: int | bool = False, code: str | None = None):
"""
If no Authorization code get it from Google and then request for Refresh Token.
Google Contact Name is set to flags to set_value after Authorization Code is obtained.
@ -88,7 +88,7 @@ def get_google_contacts_object(g_contact):
@frappe.whitelist()
def sync(g_contact=None):
def sync(g_contact: str | None = None):
filters = {"enable": 1}
if g_contact:

View file

@ -117,7 +117,7 @@ class SocialLoginKey(Document):
self.icon = f"/assets/frappe/icons/social/{icon_file}"
@frappe.whitelist()
def get_social_login_provider(self, provider, initialize=False):
def get_social_login_provider(self, provider: str, initialize: int | bool = False):
providers = {}
providers["Office 365"] = {

View file

@ -120,7 +120,7 @@ class Webhook(Document):
frappe.throw(_("Invalid Webhook Secret"))
@frappe.whitelist()
def preview_meets_condition(self, preview_document):
def preview_meets_condition(self, preview_document: str):
if not self.condition:
return _("Yes")
try:
@ -132,7 +132,7 @@ class Webhook(Document):
return _("Yes") if met_condition else _("No")
@frappe.whitelist()
def preview_request_body(self, preview_document):
def preview_request_body(self, preview_document: str):
try:
doc = frappe.get_cached_doc(self.webhook_doctype, preview_document)
return frappe.as_json(get_webhook_data(doc, self))

View file

@ -1,3 +1,5 @@
from typing import Any
import requests
import frappe
@ -60,7 +62,7 @@ def current_site_info():
@frappe.whitelist()
def api(method, data=None):
def api(method: str, data: str | dict[str, Any] | None = None):
if data is None:
data = {}
request = requests.post(

View file

@ -225,7 +225,7 @@ def get_openid_configuration():
@frappe.whitelist(allow_guest=True)
def introspect_token(token: str, token_type_hint=None):
def introspect_token(token: str, token_type_hint: str | None = None):
if token_type_hint not in ["access_token", "refresh_token"]:
token_type_hint = "access_token"
try:

View file

@ -1424,7 +1424,7 @@ class Document(BaseDocument):
self.run_method("on_discard")
@frappe.whitelist()
def rename(self, name: str | int, merge=False, force=False, validate_rename=True):
def rename(self, name: str | int, merge: bool = False, force: bool = False, validate_rename: bool = True):
"""Rename the document to `name`. This transforms the current object."""
return self._rename(name=name, merge=merge, force=force, validate_rename=validate_rename)
@ -1786,10 +1786,10 @@ class Document(BaseDocument):
@frappe.whitelist()
def add_comment(
self,
comment_type="Comment",
text=None,
comment_email=None,
comment_by=None,
comment_type: str = "Comment",
text: str | None = None,
comment_email: str | None = None,
comment_by: str | None = None,
):
"""Add a comment to this document.

View file

@ -5,11 +5,14 @@ import json
import frappe
from frappe import _
from frappe.model import child_table_fields, default_fields, table_fields
from frappe.model.document import Document
from frappe.utils import cstr
@frappe.whitelist()
def make_mapped_doc(method, source_name, selected_children=None, args=None):
def make_mapped_doc(
method: str, source_name: str, selected_children: str | None = None, args: str | None = None
):
"""Return the mapped document calling the given mapper method.
Set `selected_children` as flags for the `get_mapped_doc` method.
@ -30,7 +33,7 @@ def make_mapped_doc(method, source_name, selected_children=None, args=None):
@frappe.whitelist()
def map_docs(method, source_names, target_doc, args=None):
def map_docs(method: str, source_names: str, target_doc: Document | dict | str, args: str | None = None):
"""Return the mapped document calling the given mapper method with each of the given source docs on the target doc.
:param args: Args as string to pass to the mapper method

View file

@ -63,14 +63,14 @@ def sync_user_settings():
@frappe.whitelist()
def save(doctype, user_settings):
def save(doctype: str, user_settings: str):
user_settings = json.loads(user_settings or "{}")
update_user_settings(doctype, user_settings)
return user_settings
@frappe.whitelist()
def get(doctype):
def get(doctype: str):
return get_user_settings(doctype)

View file

@ -5,7 +5,7 @@ from __future__ import annotations
import json
from collections import defaultdict
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
import frappe
from frappe import _
@ -43,7 +43,7 @@ def get_workflow_name(doctype):
@frappe.whitelist()
def get_transitions(
doc: Document | str | dict, workflow: Workflow = None, raise_exception: bool = False
doc: Document | str | dict, workflow: Workflow | None = None, raise_exception: bool = False
) -> list[dict]:
"""Return list of possible transitions for the given doc"""
from frappe.model.document import Document
@ -117,7 +117,7 @@ def evaluate_workflow_value(value, evaluate_as_expression, doc):
@frappe.whitelist()
def apply_workflow(doc, action):
def apply_workflow(doc: Document | str | dict, action: str):
"""Allow workflow action on the current doc"""
doc = frappe.get_doc(frappe.parse_json(doc))
doc.load_from_db()
@ -228,7 +228,7 @@ def apply_workflow(doc, action):
@frappe.whitelist()
def can_cancel_document(doctype):
def can_cancel_document(doctype: str):
workflow = get_workflow(doctype)
cancelling_states = [s.state for s in workflow.states if s.doc_status == "2"]
if not cancelling_states:
@ -312,7 +312,7 @@ def get_workflow_field_value(workflow_name, field):
@frappe.whitelist()
def bulk_workflow_approval(docnames, doctype, action):
def bulk_workflow_approval(docnames: str, doctype: str, action: str):
docnames = json.loads(docnames)
if len(docnames) < 20:
_bulk_workflow_action(docnames, doctype, action)
@ -407,7 +407,7 @@ def print_workflow_log(messages, title, doctype, indicator):
@frappe.whitelist()
def get_common_transition_actions(docs, doctype):
def get_common_transition_actions(docs: str | list[dict[str, Any]], doctype: str):
common_actions = []
if isinstance(docs, str):
docs = json.loads(docs)

View file

@ -21,7 +21,7 @@ class NetworkPrinterSettings(Document):
# end: auto-generated types
@frappe.whitelist()
def get_printers_list(self, ip="127.0.0.1", port=631):
def get_printers_list(self):
printer_list = []
try:
import cups

View file

@ -2,7 +2,7 @@ import frappe
@frappe.whitelist()
def get_print_settings_to_show(doctype, docname):
def get_print_settings_to_show(doctype: str, docname: str):
doc = frappe.get_doc(doctype, docname)
print_settings = frappe.get_single("Print Settings")

View file

@ -2,7 +2,9 @@ import frappe
@frappe.whitelist()
def create_custom_format(doctype, name, based_on="Standard", beta=False):
def create_custom_format(
doctype: str, name: str | int, based_on: str = "Standard", beta: str | int | bool = False
):
doc = frappe.new_doc("Print Format")
doc.doc_type = doctype
doc.name = name

View file

@ -364,7 +364,7 @@ def stop(*args, **kwargs):
@frappe.whitelist()
@do_not_record
@administrator_only
def get(uuid=None, *args, **kwargs):
def get(uuid: str | None = None, *args, **kwargs):
if uuid:
result = frappe.cache.hget(RECORDER_REQUEST_HASH, uuid)
else:

View file

@ -19,7 +19,18 @@ if TYPE_CHECKING:
@frappe.whitelist()
def add(doctype, name, user=None, read=1, write=0, submit=0, share=0, everyone=0, notify=0, **kwargs):
def add(
doctype: str,
name: str | int,
user: str | None = None,
read: str | bool | int = 1,
write: str | bool | int = 0,
submit: str | bool | int = 0,
share: str | bool | int = 0,
everyone: str | bool | int = 0,
notify: str | bool | int = 0,
**kwargs,
):
"""Expose function without flags to the client-side"""
return add_docshare(
doctype,
@ -85,7 +96,14 @@ def remove(doctype, name, user, flags=None):
@frappe.whitelist()
def set_permission(doctype, name, user, permission_to, value=1, everyone=0):
def set_permission(
doctype: str,
name: str | int,
user: str | None,
permission_to: str,
value: str | bool | int = 1,
everyone: str | bool | int = 0,
):
"""Expose function without flags to the client-side"""
return set_docshare_permission(doctype, name, user, permission_to, value=value, everyone=everyone)
@ -246,7 +264,7 @@ def check_share_permission(doctype, name, permissions=None, custom_perms=None):
def notify_assignment(shared_by, doctype, doc_name, everyone, notify=0):
if not (shared_by and doctype and doc_name) or everyone or not notify:
if not (shared_by and doctype and doc_name) or cint(everyone) or not cint(notify):
return
from frappe.utils import get_fullname

View file

@ -25,7 +25,9 @@ def get_limit():
@frappe.whitelist(allow_guest=True)
# @rate_limit(key="reference_name", limit=get_limit, seconds=60 * 60)
def add_comment(comment, comment_email, comment_by, reference_doctype, reference_name, route):
def add_comment(
comment: str, comment_email: str, comment_by: str, reference_doctype: str, reference_name: str, route: str
):
if frappe.session.user == "Guest":
allowed_doctypes = ["Web Page"]
comments_permission_config = frappe.get_hooks("has_comment_permission")

View file

@ -249,7 +249,7 @@ class TestRenameDoc(IntegrationTestCase):
name = choice(self.available_documents)
new_name = f"{name}-{frappe.generate_hash(length=4)}"
doc = frappe.get_doc(self.test_doctype, name)
doc.rename(new_name, merge=frappe.db.exists(self.test_doctype, new_name))
doc.rename(new_name, merge=bool(frappe.db.exists(self.test_doctype, new_name)))
self.assertEqual(doc.name, new_name)
self.available_documents.append(new_name)
self.available_documents.remove(name)

View file

@ -883,7 +883,7 @@ def deduplicate_messages(messages):
@frappe.whitelist()
def update_translations_for_source(source=None, translation_dict=None):
def update_translations_for_source(source: str | None = None, translation_dict: str | None = None):
if not (source and translation_dict):
return

View file

@ -120,7 +120,7 @@ class Workflow(Document):
@frappe.whitelist()
def get_workflow_state_count(doctype, workflow_state_field, states):
def get_workflow_state_count(doctype: str, workflow_state_field: str, states: str | list[str]):
frappe.has_permission(doctype=doctype, ptype="read", throw=True)
states = frappe.parse_json(states)

View file

@ -1,6 +1,8 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
from datetime import datetime
import frappe
from frappe import _
from frappe.desk.form.utils import get_pdf_link
@ -127,7 +129,14 @@ def process_workflow_actions(doc, state):
@frappe.whitelist(allow_guest=True)
def apply_action(action, doctype, docname, current_state, user=None, last_modified=None):
def apply_action(
action: str,
doctype: str,
docname: str | int,
current_state: str,
user: str | None = None,
last_modified: str | datetime | None = None,
):
if not verify_request():
return
@ -147,7 +156,7 @@ def apply_action(action, doctype, docname, current_state, user=None, last_modifi
@frappe.whitelist(allow_guest=True)
def confirm_action(doctype, docname, user, action):
def confirm_action(doctype: str, docname: str | int, user: str, action: str):
if not verify_request():
return