diff --git a/.flake8 b/.flake8 deleted file mode 100644 index e783fbbeb3..0000000000 --- a/.flake8 +++ /dev/null @@ -1,75 +0,0 @@ -[flake8] -ignore = - B001, - B007, - B009, - B010, - B950, - E101, - E111, - E114, - E116, - E117, - E121, - E122, - E123, - E124, - E125, - E126, - E127, - E128, - E131, - E201, - E202, - E203, - E211, - E221, - E222, - E223, - E224, - E225, - E226, - E228, - E231, - E241, - E242, - E251, - E261, - E262, - E265, - E266, - E271, - E272, - E273, - E274, - E301, - E302, - E303, - E305, - E306, - E402, - E501, - E502, - E701, - E702, - E703, - E741, - F401, - F403, - F405, - W191, - W291, - W292, - W293, - W391, - W503, - W504, - E711, - E129, - F841, - E713, - E712, - B028, - -max-line-length = 200 -exclude=,test_*.py diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index b978236228..11ecbd391b 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -46,3 +46,6 @@ c35476256f85271fb57584eb0a26f4d9def3caf4 # black+isort -> ruff de9ac897482013f5464a05f3c171da0072619c3a + +# flake8 -> ruff + ruff config update +26ae0f3460f29116e0c083d57eee9f33763237ea diff --git a/.github/helper/ci.py b/.github/helper/ci.py index 10bafb3dc5..9c4e1379bb 100644 --- a/.github/helper/ci.py +++ b/.github/helper/ci.py @@ -1,8 +1,8 @@ # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See LICENSE +import json import os from pathlib import Path -import json STANDARD_INCLUSIONS = ["*.py"] diff --git a/.github/helper/documentation.py b/.github/helper/documentation.py index b541583fd6..7eb209cbde 100644 --- a/.github/helper/documentation.py +++ b/.github/helper/documentation.py @@ -1,7 +1,7 @@ import sys -import requests from urllib.parse import urlparse +import requests WEBSITE_REPOS = [ "erpnext_com", @@ -36,11 +36,7 @@ def is_documentation_link(word: str) -> bool: def contains_documentation_link(body: str) -> bool: - return any( - is_documentation_link(word) - for line in body.splitlines() - for word in line.split() - ) + return any(is_documentation_link(word) for line in body.splitlines() for word in line.split()) def check_pull_request(number: str) -> "tuple[int, str]": @@ -53,12 +49,7 @@ def check_pull_request(number: str) -> "tuple[int, str]": head_sha = (payload.get("head") or {}).get("sha") body = (payload.get("body") or "").lower() - if ( - not title.startswith("feat") - or not head_sha - or "no-docs" in body - or "backport" in body - ): + if not title.startswith("feat") or not head_sha or "no-docs" in body or "backport" in body: return 0, "Skipping documentation checks... 🏃" if contains_documentation_link(body): diff --git a/.github/helper/roulette.py b/.github/helper/roulette.py index ebca901c95..121b9ff0d6 100644 --- a/.github/helper/roulette.py +++ b/.github/helper/roulette.py @@ -6,11 +6,11 @@ import subprocess import sys import time import urllib.request -from functools import lru_cache +from functools import cache from urllib.error import HTTPError -@lru_cache(maxsize=None) +@cache def fetch_pr_data(pr_number, repo, endpoint=""): api_url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}" @@ -83,9 +83,7 @@ def is_ci(file): def is_frontend_code(file): - return file.lower().endswith( - (".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue", ".html") - ) + return file.lower().endswith((".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue", ".html")) def is_docs(file): diff --git a/.github/helper/translation.py b/.github/helper/translation.py index 72f661d3e1..96a319f97e 100644 --- a/.github/helper/translation.py +++ b/.github/helper/translation.py @@ -2,7 +2,9 @@ import re import sys errors_encounter = 0 -pattern = re.compile(r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)") +pattern = re.compile( + r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)" +) words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]") start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}") f_string_pattern = re.compile(r"_\(f[\"']") @@ -10,44 +12,50 @@ starts_with_f_pattern = re.compile(r"_\(f") # skip first argument files = sys.argv[1:] -files_to_scan = [_file for _file in files if _file.endswith(('.py', '.js'))] +files_to_scan = [_file for _file in files if _file.endswith((".py", ".js"))] for _file in files_to_scan: - with open(_file, 'r') as f: - print(f'Checking: {_file}') + with open(_file) as f: + print(f"Checking: {_file}") file_lines = f.readlines() for line_number, line in enumerate(file_lines, 1): - if 'frappe-lint: disable-translate' in line: + if "frappe-lint: disable-translate" in line: continue if start_matches := start_pattern.search(line): if starts_with_f := starts_with_f_pattern.search(line): if has_f_string := f_string_pattern.search(line): errors_encounter += 1 - print(f'\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}') + print( + f"\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}" + ) continue match = pattern.search(line) error_found = False - if not match and line.endswith((',\n', '[\n')): + if not match and line.endswith((",\n", "[\n")): # concat remaining text to validate multiline pattern - line = "".join(file_lines[line_number - 1:]) - line = line[start_matches.start() + 1:] + line = "".join(file_lines[line_number - 1 :]) + line = line[start_matches.start() + 1 :] match = pattern.match(line) if not match: error_found = True - print(f'\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}') + print(f"\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}") if not error_found and not words_pattern.search(line): error_found = True - print(f'\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}') + print( + f"\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}" + ) if error_found: errors_encounter += 1 if errors_encounter > 0: - print('\nVisit "https://frappeframework.com/docs/user/en/translations" to learn about valid translation strings.') + print( + '\nVisit "https://frappeframework.com/docs/user/en/translations" to learn about valid translation strings.' + ) sys.exit(1) else: - print('\nGood To Go!') + print("\nGood To Go!") diff --git a/.mergify.yml b/.mergify.yml index 7c524c5e93..66160a79e4 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -21,27 +21,6 @@ pull_request_rules: @{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on an appropriate hotfix branch. https://github.com/frappe/erpnext/wiki/Pull-Request-Checklist#which-branch - - name: Automatic merge on CI success and review - conditions: - - label!=dont-merge - - label!=squash - - "#approved-reviews-by>=1" - actions: - merge: - method: merge - - name: Automatic squash on CI success and review - conditions: - - label!=dont-merge - - label=squash - - "#approved-reviews-by>=1" - actions: - merge: - method: squash - commit_message_template: | - {{ title }} (#{{ number }}) - - {{ body }} - - name: backport to develop conditions: - label="backport develop" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1e9e2adb90..bc20e86eba 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,18 +20,12 @@ repos: - id: check-yaml - id: debug-statements - - repo: https://github.com/asottile/pyupgrade - rev: v3.9.0 - hooks: - - id: pyupgrade - args: ['--py310-plus'] - - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.2.0 hooks: - id: ruff - name: "Sort Python imports" - args: ["--select", "I", "--fix"] + name: "Run ruff linter and apply fixes" + args: ["--fix"] - id: ruff-format name: "Format Python code" @@ -72,12 +66,6 @@ repos: frappe/public/js/lib/.* )$ - - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 - hooks: - - id: flake8 - additional_dependencies: ['flake8-bugbear',] - ci: autoupdate_schedule: weekly skip: [] diff --git a/frappe/__init__.py b/frappe/__init__.py index cca8f12198..ff953cce26 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -152,12 +152,12 @@ class _LazyTranslate: return self.value def __add__(self, other): - if isinstance(other, (str, _LazyTranslate)): + if isinstance(other, str | _LazyTranslate): return self.value + str(other) raise NotImplementedError def __radd__(self, other): - if isinstance(other, (str, _LazyTranslate)): + if isinstance(other, str | _LazyTranslate): return str(other) + self.value return NotImplementedError @@ -1300,7 +1300,7 @@ def get_cached_value(doctype: str, name: str, fieldname: str = "name", as_dict: values = [doc.get(f) for f in fieldname] if as_dict: - return _dict(zip(fieldname, values)) + return _dict(zip(fieldname, values, strict=False)) return values @@ -1644,7 +1644,7 @@ def _load_app_hooks(app_name: str | None = None): raise def _is_valid_hook(obj): - return not isinstance(obj, (types.ModuleType, types.FunctionType, type)) + return not isinstance(obj, types.ModuleType | types.FunctionType | type) for key, value in inspect.getmembers(app_hooks, predicate=_is_valid_hook): if not key.startswith("_"): @@ -1904,7 +1904,7 @@ def copy_doc(doc: "Document", ignore_no_copy: bool = True) -> "Document": if not ignore_no_copy: remove_no_copy_fields(newdoc) - for i, d in enumerate(newdoc.get_all_children()): + for d in newdoc.get_all_children(): d.set("__islocal", 1) for fieldname in fields_to_clear: @@ -2493,7 +2493,7 @@ def mock(type, size=1, locale="en"): if type not in dir(fake): raise ValueError("Not a valid mock type.") else: - for i in range(size): + for _ in range(size): data = getattr(fake, type)() results.append(data) @@ -2507,7 +2507,7 @@ def validate_and_sanitize_search_inputs(fn): def wrapper(*args, **kwargs): from frappe.desk.search import sanitize_searchfield - kwargs.update(dict(zip(fn.__code__.co_varnames, args))) + kwargs.update(dict(zip(fn.__code__.co_varnames, args, strict=False))) sanitize_searchfield(kwargs["searchfield"]) kwargs["start"] = cint(kwargs["start"]) kwargs["page_len"] = cint(kwargs["page_len"]) @@ -2520,7 +2520,7 @@ def validate_and_sanitize_search_inputs(fn): return wrapper -from frappe.utils.error import log_error # noqa: backward compatibility +from frappe.utils.error import log_error # noqa if _tune_gc: # generational GC gets triggered after certain allocs (g0) which is 700 by default. diff --git a/frappe/auth.py b/frappe/auth.py index ae19e0558b..ee8ec24104 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -376,7 +376,7 @@ class CookieManager: } def delete_cookie(self, to_delete): - if not isinstance(to_delete, (list, tuple)): + if not isinstance(to_delete, list | tuple): to_delete = [to_delete] self.to_delete.extend(to_delete) diff --git a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py index ed5fca7902..da123840aa 100644 --- a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py @@ -104,7 +104,7 @@ class TestAutoAssign(FrappeTestCase): frappe.db.delete("ToDo", {"name": d.name}) # add 5 more assignments - for i in range(5): + for _ in range(5): _make_test_record(public=1) # check if each user still has 10 assignments diff --git a/frappe/build.py b/frappe/build.py index 933799b2ce..40d9c98ed5 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -177,9 +177,6 @@ def symlink(target, link_name, overwrite=False): if not overwrite: return os.symlink(target, link_name) - # os.replace() may fail if files are on different filesystems - link_dir = os.path.dirname(link_name) - # Create link to target with temporary filename while True: temp_link_name = f"tmp{frappe.generate_hash()}" diff --git a/frappe/commands/site.py b/frappe/commands/site.py index 28932ba5e5..832364ab92 100644 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -1036,7 +1036,7 @@ def _drop_site( f"Error: The operation has stopped because backup of {site}'s database failed.", f"Reason: {str(err)}\n", "Fix the issue and try again.", - "Hint: Use 'bench drop-site {0} --force' to force the removal of {0}".format(site), + f"Hint: Use 'bench drop-site {site} --force' to force the removal of {site}", ] click.echo("\n".join(messages)) sys.exit(1) diff --git a/frappe/commands/translate.py b/frappe/commands/translate.py index 5ebae3bfb8..361a19aee2 100644 --- a/frappe/commands/translate.py +++ b/frappe/commands/translate.py @@ -38,9 +38,7 @@ def new_language(context, lang_code, app): frappe.connect() frappe.translate.write_translations_file(app, lang_code) - print( - "File created at ./apps/{app}/{app}/translations/{lang_code}.csv".format(app=app, lang_code=lang_code) - ) + print(f"File created at ./apps/{app}/{app}/translations/{lang_code}.csv") print("You will need to add the language in frappe/geo/languages.json, if you haven't done it already.") diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 6151a844e0..e4b4118c37 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -556,7 +556,7 @@ def jupyter(context): os.mkdir(jupyter_notebooks_path) bin_path = os.path.abspath("../env/bin") print( - """ + f""" Starting Jupyter notebook Run the following in your first cell to connect notebook to frappe ``` @@ -566,7 +566,7 @@ frappe.connect() frappe.local.lang = frappe.db.get_default('lang') frappe.db.connect() ``` - """.format(site=site, sites_path=sites_path) + """ ) os.execv( f"{bin_path}/jupyter", @@ -791,7 +791,12 @@ def run_tests( @click.option("--app", help="For App", default="frappe") @click.option("--build-number", help="Build number", default=1) @click.option("--total-builds", help="Total number of builds", default=1) -@click.option("--with-coverage", is_flag=True, help="Build coverage file") +@click.option( + "--with-coverage", + is_flag=True, + help="Build coverage file", + envvar="CAPTURE_COVERAGE", +) @click.option("--use-orchestrator", is_flag=True, help="Use orchestrator to run parallel tests") @click.option("--dry-run", is_flag=True, default=False, help="Dont actually run tests") @pass_context diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index bd57b44a1b..efb53dd590 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -125,11 +125,10 @@ def get_preferred_address(doctype, name, preferred_key="is_primary_address"): FROM `tabAddress` addr, `tabDynamic Link` dl WHERE - dl.parent = addr.name and dl.link_doctype = %s and - dl.link_name = %s and ifnull(addr.disabled, 0) = 0 and - %s = %s - """ - % ("%s", "%s", preferred_key, "%s"), + dl.parent = addr.name and dl.link_doctype = {} and + dl.link_name = {} and ifnull(addr.disabled, 0) = 0 and + {} = {} + """.format("%s", "%s", preferred_key, "%s"), (doctype, name, 1), as_dict=1, ) diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index 8d549d9c5a..242b08561e 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -256,7 +256,7 @@ def contact_query(doctype, txt, searchfield, start, page_len, filters): link_name = filters.pop("link_name") return frappe.db.sql( - """select + f"""select `tabContact`.name, `tabContact`.full_name, `tabContact`.company_name from `tabContact`, `tabDynamic Link` @@ -265,12 +265,12 @@ def contact_query(doctype, txt, searchfield, start, page_len, filters): `tabDynamic Link`.parenttype = 'Contact' and `tabDynamic Link`.link_doctype = %(link_doctype)s and `tabDynamic Link`.link_name = %(link_name)s and - `tabContact`.`{key}` like %(txt)s - {mcond} + `tabContact`.`{searchfield}` like %(txt)s + {get_match_cond(doctype)} order by if(locate(%(_txt)s, `tabContact`.full_name), locate(%(_txt)s, `tabContact`.company_name), 99999), `tabContact`.idx desc, `tabContact`.full_name - limit %(start)s, %(page_len)s """.format(mcond=get_match_cond(doctype), key=searchfield), + limit %(start)s, %(page_len)s """, { "txt": "%" + txt + "%", "_txt": txt.replace("%", ""), diff --git a/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py b/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py index 9bb661d90c..12d31b158f 100644 --- a/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py +++ b/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py @@ -52,7 +52,6 @@ def get_columns(filters): def get_data(filters): - data = [] reference_doctype = filters.get("reference_doctype") reference_name = filters.get("reference_name") diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index b24f7368b4..68372ea7af 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -53,7 +53,7 @@ class ActivityLog(Document): def set_ip_address(self): if self.operation in ("Login", "Logout"): - self.ip_address = getattr(frappe.local, "request_ip") + self.ip_address = frappe.local.request_ip @staticmethod def clear_old_logs(days=None): diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 9b1ec3fa4b..468f7ea6ea 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -556,6 +556,7 @@ def get_contacts(email_strings: list[str], auto_create_contact=False) -> list[st contact.insert(ignore_permissions=True) contact_name = contact.name except Exception: + contact_name = None contact.log_error("Unable to add contact") if contact_name: diff --git a/frappe/core/doctype/communication/communication_list.js b/frappe/core/doctype/communication/communication_list.js index 4ef3a384ff..8f2623ca09 100644 --- a/frappe/core/doctype/communication/communication_list.js +++ b/frappe/core/doctype/communication/communication_list.js @@ -13,8 +13,6 @@ frappe.listview_settings["Communication"] = { "communication_date", ], - filters: [["status", "=", "Open"]], - onload: function (list_view) { let method = "frappe.email.inbox.create_email_flag_queue"; diff --git a/frappe/core/doctype/data_import/data_import.py b/frappe/core/doctype/data_import/data_import.py index 8504aa1b04..ab9dfa9372 100644 --- a/frappe/core/doctype/data_import/data_import.py +++ b/frappe/core/doctype/data_import/data_import.py @@ -271,7 +271,7 @@ def export_json(doctype, path, filters=None, or_filters=None, name=None, order_b for key in del_keys: if key in doc: del doc[key] - for k, v in doc.items(): + for v in doc.values(): if isinstance(v, list): for child in v: for key in del_keys + ("docstatus", "doctype", "modified", "name"): diff --git a/frappe/core/doctype/data_import/importer.py b/frappe/core/doctype/data_import/importer.py index c83d60ea8b..fe4b1bc169 100644 --- a/frappe/core/doctype/data_import/importer.py +++ b/frappe/core/doctype/data_import/importer.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE -import io import json import os import re @@ -149,8 +148,8 @@ class Importer: if self.console: update_progress_bar( - f"Importing {total_payload_count} records", - current_index, + f"Importing {self.doctype}: {total_payload_count} records", + current_index - 1, total_payload_count, ) elif total_payload_count > 5: @@ -525,7 +524,6 @@ class ImportFile: # subsequent rows that have blank values in parent columns # are considered as child rows parent_column_indexes = self.header.get_column_indexes(self.doctype) - parent_row_values = first_row.get_values(parent_column_indexes) data_without_first_row = data[1:] for row in data_without_first_row: @@ -656,7 +654,7 @@ class Row: for key in frappe.model.default_fields + frappe.model.child_table_fields + ("__islocal",): doc.pop(key, None) - for col, value in zip(columns, values): + for col, value in zip(columns, values, strict=False): df = col.df if value in INVALID_VALUES: value = None @@ -751,7 +749,7 @@ class Row: def parse_value(self, value, col): df = col.df - if isinstance(value, (datetime, date)) and df.fieldtype in ["Date", "Datetime"]: + if isinstance(value, datetime | date) and df.fieldtype in ["Date", "Datetime"]: return value value = cstr(value) @@ -774,7 +772,7 @@ class Row: return value def get_date(self, value, column): - if isinstance(value, (datetime, date)): + if isinstance(value, datetime | date): return value date_format = column.date_format @@ -938,7 +936,7 @@ class Column: """ def guess_date_format(d): - if isinstance(d, (datetime, date, time)): + if isinstance(d, datetime | date | time): if self.df.fieldtype == "Date": return "%Y-%m-%d" if self.df.fieldtype == "Datetime": @@ -1137,7 +1135,6 @@ def build_fields_dict_for_column_matching(parent_doctype): label = (df.label or "").strip() translated_label = _(label) - parent = df.parent or parent_doctype if parent_doctype == doctype: # for parent doctypes keys will be diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index fdd6f51e9c..40b3b31b61 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1363,11 +1363,9 @@ def validate_fields(meta): if not d.get("__islocal") and frappe.db.has_column(d.parent, d.fieldname): has_non_unique_values = frappe.db.sql( - """select `{fieldname}`, count(*) - from `tab{doctype}` where ifnull(`{fieldname}`, '') != '' - group by `{fieldname}` having count(*) > 1 limit 1""".format( - doctype=d.parent, fieldname=d.fieldname - ) + f"""select `{d.fieldname}`, count(*) + from `tab{d.parent}` where ifnull(`{d.fieldname}`, '') != '' + group by `{d.fieldname}` having count(*) > 1 limit 1""" ) if has_non_unique_values and has_non_unique_values[0][0]: @@ -1541,7 +1539,7 @@ def validate_fields(meta): field.options = "\n".join(options_list) def scrub_fetch_from(field): - if hasattr(field, "fetch_from") and getattr(field, "fetch_from"): + if hasattr(field, "fetch_from") and field.fetch_from: field.fetch_from = field.fetch_from.strip("\n").strip() def validate_data_field_type(docfield): @@ -1826,7 +1824,7 @@ def make_module_and_roles(doc, perm_fieldname="permissions"): r.desk_access = 1 r.flags.ignore_mandatory = r.flags.ignore_permissions = True r.insert() - except frappe.DoesNotExistError as e: + except frappe.DoesNotExistError: pass except frappe.db.ProgrammingError as e: if frappe.db.is_table_missing(e): diff --git a/frappe/core/doctype/error_log/test_error_log.py b/frappe/core/doctype/error_log/test_error_log.py index 22eeea329e..98c87dda52 100644 --- a/frappe/core/doctype/error_log/test_error_log.py +++ b/frappe/core/doctype/error_log/test_error_log.py @@ -52,15 +52,21 @@ _THROW_EXC = """ frappe.exceptions.ValidationError: what """ -TEST_EXCEPTIONS = { - "erpnext (app)": _RAW_EXC, - "erpnext (app)": _THROW_EXC, -} +TEST_EXCEPTIONS = ( + ( + "erpnext (app)", + _RAW_EXC, + ), + ( + "erpnext (app)", + _THROW_EXC, + ), +) class TestExceptionSourceGuessing(FrappeTestCase): @patch.object(frappe, "get_installed_apps", return_value=["frappe", "erpnext", "3pa"]) def test_exc_source_guessing(self, _installed_apps): - for source, exc in TEST_EXCEPTIONS.items(): + for source, exc in TEST_EXCEPTIONS: result = guess_exception_source(exc) self.assertEqual(result, source) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 9d4b8f6a4a..d203894cce 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -137,7 +137,7 @@ class File(Document): if not self.attached_to_doctype: return - if not self.attached_to_name or not isinstance(self.attached_to_name, (str, int)): + if not self.attached_to_name or not isinstance(self.attached_to_name, str | int): frappe.throw(_("Attached To Name must be a string or an integer"), frappe.ValidationError) if self.attached_to_field and SPECIAL_CHAR_PATTERN.search(self.attached_to_field): diff --git a/frappe/core/doctype/page/page.py b/frappe/core/doctype/page/page.py index 872b6c0012..48d783f14e 100644 --- a/frappe/core/doctype/page/page.py +++ b/frappe/core/doctype/page/page.py @@ -88,14 +88,13 @@ class Page(Document): if not os.path.exists(path + ".js"): with open(path + ".js", "w") as f: f.write( - """frappe.pages['%s'].on_page_load = function(wrapper) { - var page = frappe.ui.make_app_page({ + f"""frappe.pages['{self.name}'].on_page_load = function(wrapper) {{ + var page = frappe.ui.make_app_page({{ parent: wrapper, - title: '%s', + title: '{self.title}', single_column: true - }); -}""" - % (self.name, self.title) + }}); +}}""" ) def as_dict(self, no_nulls=False): diff --git a/frappe/core/doctype/report/report.py b/frappe/core/doctype/report/report.py index e14ec41fde..c108c8ee6e 100644 --- a/frappe/core/doctype/report/report.py +++ b/frappe/core/doctype/report/report.py @@ -301,7 +301,7 @@ class Report(Document): if filters: for key, value in filters.items(): condition, _value = "=", value - if isinstance(value, (list, tuple)): + if isinstance(value, list | tuple): condition, _value = value _filters.append([key, condition, _value]) @@ -360,7 +360,7 @@ class Report(Document): def build_data_dict(self, result, columns): data = [] for row in result: - if isinstance(row, (list, tuple)): + if isinstance(row, list | tuple): _row = frappe._dict() for i, val in enumerate(row): _row[columns[i].get("fieldname")] = val diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index cda004c00c..b22f4b491c 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -131,7 +131,12 @@ class TestReport(FrappeTestCase): self.assertListEqual(["email"], [column.get("fieldname") for column in columns]) admin_dict = frappe.core.utils.find(result, lambda d: d["name"] == "Administrator") self.assertDictEqual( - {"name": "Administrator", "user_type": "System User", "email": "admin@example.com"}, admin_dict + { + "name": "Administrator", + "user_type": "System User", + "email": "admin@example.com", + }, + admin_dict, ) def test_report_with_custom_column(self): @@ -156,10 +161,18 @@ class TestReport(FrappeTestCase): ) result = response.get("result") columns = response.get("columns") - self.assertListEqual(["name", "email", "user_type"], [column.get("fieldname") for column in columns]) + self.assertListEqual( + ["name", "email", "user_type"], + [column.get("fieldname") for column in columns], + ) admin_dict = frappe.core.utils.find(result, lambda d: d["name"] == "Administrator") self.assertDictEqual( - {"name": "Administrator", "user_type": "System User", "email": "admin@example.com"}, admin_dict + { + "name": "Administrator", + "user_type": "System User", + "email": "admin@example.com", + }, + admin_dict, ) def test_report_permissions(self): @@ -167,9 +180,7 @@ class TestReport(FrappeTestCase): frappe.db.delete("Has Role", {"parent": frappe.session.user, "role": "Test Has Role"}) frappe.db.commit() if not frappe.db.exists("Role", "Test Has Role"): - role = frappe.get_doc({"doctype": "Role", "role_name": "Test Has Role"}).insert( - ignore_permissions=True - ) + frappe.get_doc({"doctype": "Role", "role_name": "Test Has Role"}).insert(ignore_permissions=True) if not frappe.db.exists("Report", "Test Report"): report = frappe.get_doc( @@ -253,18 +264,18 @@ class TestReport(FrappeTestCase): report.report_script = """ totals = {} for user in frappe.get_all('User', fields = ['name', 'user_type', 'creation']): - if not user.user_type in totals: - totals[user.user_type] = 0 - totals[user.user_type] = totals[user.user_type] + 1 + if not user.user_type in totals: + totals[user.user_type] = 0 + totals[user.user_type] = totals[user.user_type] + 1 data = [ - [ - {'fieldname': 'type', 'label': 'Type'}, - {'fieldname': 'value', 'label': 'Value'} - ], - [ - {"type":key, "value": value} for key, value in totals.items() - ] + [ + {'fieldname': 'type', 'label': 'Type'}, + {'fieldname': 'value', 'label': 'Value'} + ], + [ + {"type":key, "value": value} for key, value in totals.items() + ] ] """ report.save() @@ -299,13 +310,13 @@ data = [ report.report_script = """ totals = {} for user in frappe.get_all('User', fields = ['name', 'user_type', 'creation']): - if not user.user_type in totals: - totals[user.user_type] = 0 - totals[user.user_type] = totals[user.user_type] + 1 + if not user.user_type in totals: + totals[user.user_type] = 0 + totals[user.user_type] = totals[user.user_type] + 1 result = [ - {"type":key, "value": value} for key, value in totals.items() - ] + {"type":key, "value": value} for key, value in totals.items() + ] """ report.save() @@ -344,15 +355,40 @@ result = [ report_settings = {"tree": True, "parent_field": "parent_value"} columns = [ - {"fieldname": "parent_column", "label": "Parent Column", "fieldtype": "Data", "width": 10}, - {"fieldname": "column_1", "label": "Column 1", "fieldtype": "Float", "width": 10}, - {"fieldname": "column_2", "label": "Column 2", "fieldtype": "Float", "width": 10}, + { + "fieldname": "parent_column", + "label": "Parent Column", + "fieldtype": "Data", + "width": 10, + }, + { + "fieldname": "column_1", + "label": "Column 1", + "fieldtype": "Float", + "width": 10, + }, + { + "fieldname": "column_2", + "label": "Column 2", + "fieldtype": "Float", + "width": 10, + }, ] result = [ {"parent_column": "Parent 1", "column_1": 200, "column_2": 150.50}, - {"parent_column": "Child 1", "column_1": 100, "column_2": 75.25, "parent_value": "Parent 1"}, - {"parent_column": "Child 2", "column_1": 100, "column_2": 75.25, "parent_value": "Parent 1"}, + { + "parent_column": "Child 1", + "column_1": 100, + "column_2": 75.25, + "parent_value": "Parent 1", + }, + { + "parent_column": "Child 2", + "column_1": 100, + "column_2": 75.25, + "parent_value": "Parent 1", + }, ] result = add_total_row( @@ -369,13 +405,13 @@ result = [ def test_cte_in_query_report(self): cte_query = textwrap.dedent( """ - with enabled_users as ( - select name - from `tabUser` - where enabled = 1 - ) - select * from enabled_users; - """ + with enabled_users as ( + select name + from `tabUser` + where enabled = 1 + ) + select * from enabled_users; + """ ) report = frappe.get_doc( diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index a995f9d57b..d5b405b96d 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -210,7 +210,7 @@ class ServerScript(Document): if key.startswith("_"): continue value = obj[key] - if isinstance(value, (NamespaceDict, dict)) and value: + if isinstance(value, NamespaceDict | dict) and value: if key == "form_dict": out.append(["form_dict", 7]) continue @@ -222,7 +222,7 @@ class ServerScript(Document): score = 0 elif isinstance(value, ModuleType): score = 10 - elif isinstance(value, (FunctionType, MethodType)): + elif isinstance(value, FunctionType | MethodType): score = 9 elif isinstance(value, type): score = 8 diff --git a/frappe/core/doctype/server_script/test_server_script.py b/frappe/core/doctype/server_script/test_server_script.py index cbf3ef977a..e7735c8fc9 100644 --- a/frappe/core/doctype/server_script/test_server_script.py +++ b/frappe/core/doctype/server_script/test_server_script.py @@ -218,7 +218,7 @@ frappe.qb.from_(todo).select(todo.name).where(todo.name == "{todo.name}").run() name="test_nested_scripts_1", script_type="API", api_method="test_nested_scripts_1", - script=f"""log("nothing")""", + script="""log("nothing")""", ) script.insert() script.execute_method() @@ -228,7 +228,7 @@ frappe.qb.from_(todo).select(todo.name).where(todo.name == "{todo.name}").run() name="test_nested_scripts_2", script_type="API", api_method="test_nested_scripts_2", - script=f"""frappe.call("test_nested_scripts_1")""", + script="""frappe.call("test_nested_scripts_1")""", ) script.insert() script.execute_method() diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 052e4979ec..063f9cc118 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -602,10 +602,9 @@ class User(Document): has_fields = [d.get("name") for d in desc if d.get("name") in ["owner", "modified_by"]] for field in has_fields: frappe.db.sql( - """UPDATE `%s` - SET `%s` = %s - WHERE `%s` = %s""" - % (tab, field, "%s", field, "%s"), + """UPDATE `{}` + SET `{}` = {} + WHERE `{}` = {}""".format(tab, field, "%s", field, "%s"), (new_name, old_name), ) @@ -648,7 +647,7 @@ class User(Document): def ensure_unique_roles(self): exists = [] - for i, d in enumerate(self.get("roles")): + for d in self.get("roles"): if (not d.role) or (d.role in exists): self.get("roles").remove(d) else: @@ -909,6 +908,7 @@ def test_password_strength(new_password: str, key=None, old_password=None, user_ password_policy_validation_passed = True result["feedback"]["password_policy_validation_passed"] = password_policy_validation_passed + result.pop("password", None) return result diff --git a/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py b/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py index b865c23b11..bf1694b164 100644 --- a/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py +++ b/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py @@ -36,11 +36,7 @@ def get_columns_and_fields(doctype): if df.in_list_view and df.fieldtype in data_fieldtypes: fields.append(f"`{df.fieldname}`") fieldtype = f"Link/{df.options}" if df.fieldtype == "Link" else df.fieldtype - columns.append( - "{label}:{fieldtype}:{width}".format( - label=df.label, fieldtype=fieldtype, width=df.width or 100 - ) - ) + columns.append(f"{df.label}:{fieldtype}:{df.width or 100}") return columns, fields diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index f0f415474c..7d374785dc 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -592,11 +592,11 @@ class CustomizeForm(Document): max_length = cint(frappe.db.type_map.get(df.fieldtype)[1]) fieldname = df.fieldname docs = frappe.db.sql( - """ + f""" SELECT name, {fieldname}, LENGTH({fieldname}) AS len - FROM `tab{doctype}` + FROM `tab{self.doc_type}` WHERE LENGTH({fieldname}) > {max_length} - """.format(fieldname=fieldname, doctype=self.doc_type, max_length=max_length), + """, as_dict=True, ) label = df.label diff --git a/frappe/custom/report/audit_system_hooks/audit_system_hooks.py b/frappe/custom/report/audit_system_hooks/audit_system_hooks.py index 0c3b495271..07b8e18b4d 100644 --- a/frappe/custom/report/audit_system_hooks/audit_system_hooks.py +++ b/frappe/custom/report/audit_system_hooks/audit_system_hooks.py @@ -35,7 +35,7 @@ def get_data(): v = delist(v) - if isinstance(v, (dict, list)): + if isinstance(v, dict | list): try: return frappe.as_json(v) except Exception: diff --git a/frappe/database/database.py b/frappe/database/database.py index 83b426236f..83f8e5bbcf 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -103,8 +103,8 @@ class Database: def connect(self): """Connects to a database as set in `site_config.json`.""" - self._conn: Union["MariadbConnection", "PostgresConnection"] = self.get_connection() - self._cursor: Union["MariadbCursor", "PostgresCursor"] = self._conn.cursor() + self._conn: "MariadbConnection" | "PostgresConnection" = self.get_connection() + self._cursor: "MariadbCursor" | "PostgresCursor" = self._conn.cursor() try: if execution_timeout := get_query_execution_timeout(): @@ -183,7 +183,7 @@ class Database: {"name": "a%", "owner":"test@example.com"}) """ - if isinstance(query, (MySQLQueryBuilder, PostgreSQLQueryBuilder)): + if isinstance(query, MySQLQueryBuilder | PostgreSQLQueryBuilder): frappe.errprint("Use run method to execute SQL queries generated by Query Engine") debug = debug or getattr(self, "debug", False) @@ -212,7 +212,7 @@ class Database: if values == EmptyQueryValues: values = None - elif not isinstance(values, (tuple, dict, list)): + elif not isinstance(values, tuple | dict | list): values = (values,) query, values = self._transform_query(query, values) @@ -302,7 +302,7 @@ class Database: elif as_dict: keys = [column[0] for column in self._cursor.description] for row in result: - row = frappe._dict(zip(keys, row)) + row = frappe._dict(zip(keys, row, strict=False)) if update: row.update(update) yield row @@ -371,7 +371,7 @@ class Database: return query % { k: frappe.db.escape(v) if isinstance(v, str) else v for k, v in values.items() } - elif isinstance(values, (list, tuple)): + elif isinstance(values, list | tuple): return query % tuple(frappe.db.escape(v) if isinstance(v, str) else v for v in values) return query, values @@ -438,7 +438,7 @@ class Database: if result: keys = [column[0] for column in self._cursor.description] - return [frappe._dict(zip(keys, row)) for row in result] + return [frappe._dict(zip(keys, row, strict=False)) for row in result] @staticmethod def clear_db_table_cache(query): @@ -621,8 +621,11 @@ class Database: skip_locked=skip_locked, ) except Exception as e: - if ignore and (frappe.db.is_missing_column(e) or frappe.db.is_table_missing(e)): - # table or column not found, return None + if ignore and ( + frappe.db.is_missing_column(e) + or frappe.db.is_table_missing(e) + or str(e).startswith("Invalid DocType") + ): out = None elif (not ignore) and frappe.db.is_table_missing(e): # table not found, look in singles diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py index 1c07d77d26..908e7bb479 100644 --- a/frappe/database/mariadb/database.py +++ b/frappe/database/mariadb/database.py @@ -291,18 +291,18 @@ class MariaDBDatabase(MariaDBConnectionUtil, MariaDBExceptionUtil, Database): def create_global_search_table(self): if "__global_search" not in self.get_tables(): self.sql( - """create table __global_search( + f"""create table __global_search( doctype varchar(100), - name varchar({0}), - title varchar({0}), + name varchar({self.VARCHAR_LEN}), + title varchar({self.VARCHAR_LEN}), content text, fulltext(content), - route varchar({0}), + route varchar({self.VARCHAR_LEN}), published int(1) not null default 0, unique `doctype_name` (doctype, name)) COLLATE=utf8mb4_unicode_ci ENGINE=MyISAM - CHARACTER SET=utf8mb4""".format(self.VARCHAR_LEN) + CHARACTER SET=utf8mb4""" ) def create_user_settings_table(self): @@ -322,7 +322,7 @@ class MariaDBDatabase(MariaDBConnectionUtil, MariaDBExceptionUtil, Database): def get_table_columns_description(self, table_name): """Return list of columns with descriptions.""" return self.sql( - """select + f"""select column_name as 'name', column_type as 'type', column_default as 'default', @@ -338,7 +338,7 @@ class MariaDBDatabase(MariaDBConnectionUtil, MariaDBExceptionUtil, Database): column_key = 'UNI' as 'unique', (is_nullable = 'NO') AS 'not_nullable' from information_schema.columns as columns - where table_name = '{table_name}' """.format(table_name=table_name), + where table_name = '{table_name}' """, as_dict=1, ) @@ -359,8 +359,8 @@ class MariaDBDatabase(MariaDBConnectionUtil, MariaDBExceptionUtil, Database): def has_index(self, table_name, index_name): return self.sql( - """SHOW INDEX FROM `{table_name}` - WHERE Key_name='{index_name}'""".format(table_name=table_name, index_name=index_name) + f"""SHOW INDEX FROM `{table_name}` + WHERE Key_name='{index_name}'""" ) def get_column_index(self, table_name: str, fieldname: str, unique: bool = False) -> frappe._dict | None: @@ -401,9 +401,8 @@ class MariaDBDatabase(MariaDBConnectionUtil, MariaDBExceptionUtil, Database): if not self.has_index(table_name, index_name): self.commit() self.sql( - """ALTER TABLE `%s` - ADD INDEX `%s`(%s)""" - % (table_name, index_name, ", ".join(fields)) + """ALTER TABLE `{}` + ADD INDEX `{}`({})""".format(table_name, index_name, ", ".join(fields)) ) def add_unique(self, doctype, fields, constraint_name=None): @@ -419,9 +418,8 @@ class MariaDBDatabase(MariaDBConnectionUtil, MariaDBExceptionUtil, Database): ): self.commit() self.sql( - """alter table `tab%s` - add unique `%s`(%s)""" - % (doctype, constraint_name, ", ".join(fields)) + """alter table `tab{}` + add unique `{}`({})""".format(doctype, constraint_name, ", ".join(fields)) ) def updatedb(self, doctype, meta=None): diff --git a/frappe/database/mariadb/setup_db.py b/frappe/database/mariadb/setup_db.py index 5ce6e47a42..96e80e741c 100644 --- a/frappe/database/mariadb/setup_db.py +++ b/frappe/database/mariadb/setup_db.py @@ -113,10 +113,7 @@ def check_database_settings(): result = True for key, expected_value in REQUIRED_MARIADB_CONFIG.items(): if mariadb_variables.get(key) != expected_value: - print( - "For key %s. Expected value %s, found value %s" - % (key, expected_value, mariadb_variables.get(key)) - ) + print(f"For key {key}. Expected value {expected_value}, found value {mariadb_variables.get(key)}") result = False if not result: diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index a0030432dc..003455b5dd 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -288,14 +288,14 @@ class PostgresDatabase(PostgresExceptionUtil, Database): def create_global_search_table(self): if "__global_search" not in self.get_tables(): self.sql( - """create table "__global_search"( + f"""create table "__global_search"( doctype varchar(100), - name varchar({0}), - title varchar({0}), + name varchar({self.VARCHAR_LEN}), + title varchar({self.VARCHAR_LEN}), content text, - route varchar({0}), + route varchar({self.VARCHAR_LEN}), published int not null default 0, - unique (doctype, name))""".format(self.VARCHAR_LEN) + unique (doctype, name))""" ) def create_user_settings_table(self): @@ -338,8 +338,8 @@ class PostgresDatabase(PostgresExceptionUtil, Database): def has_index(self, table_name, index_name): return self.sql( - """SELECT 1 FROM pg_indexes WHERE tablename='{table_name}' - and indexname='{index_name}' limit 1""".format(table_name=table_name, index_name=index_name) + f"""SELECT 1 FROM pg_indexes WHERE tablename='{table_name}' + and indexname='{index_name}' limit 1""" ) def add_index(self, doctype: str, fields: list, index_name: str = None): @@ -368,16 +368,15 @@ class PostgresDatabase(PostgresExceptionUtil, Database): ): self.commit() self.sql( - """ALTER TABLE `tab%s` - ADD CONSTRAINT %s UNIQUE (%s)""" - % (doctype, constraint_name, ", ".join(fields)) + """ALTER TABLE `tab{}` + ADD CONSTRAINT {} UNIQUE ({})""".format(doctype, constraint_name, ", ".join(fields)) ) def get_table_columns_description(self, table_name): """Return list of columns with description.""" # pylint: disable=W1401 return self.sql( - """ + f""" SELECT a.column_name AS name, CASE LOWER(a.data_type) WHEN 'character varying' THEN CONCAT('varchar(', a.character_maximum_length ,')') @@ -398,7 +397,7 @@ class PostgresDatabase(PostgresExceptionUtil, Database): ON SUBSTRING(b.indexdef, '(.*)') LIKE CONCAT('%', a.column_name, '%') WHERE a.table_name = '{table_name}' GROUP BY a.column_name, a.data_type, a.column_default, a.character_maximum_length, a.is_nullable; - """.format(table_name=table_name), + """, as_dict=1, ) @@ -441,7 +440,7 @@ def modify_query(query): def modify_values(values): def modify_value(value): - if isinstance(value, (list, tuple)): + if isinstance(value, list | tuple): value = tuple(modify_values(value)) elif isinstance(value, int): @@ -455,7 +454,7 @@ def modify_values(values): if isinstance(values, dict): for k, v in values.items(): values[k] = modify_value(v) - elif isinstance(values, (tuple, list)): + elif isinstance(values, tuple | list): new_values = [] for val in values: new_values.append(modify_value(val)) diff --git a/frappe/database/postgres/schema.py b/frappe/database/postgres/schema.py index 18870e299d..018de93f41 100644 --- a/frappe/database/postgres/schema.py +++ b/frappe/database/postgres/schema.py @@ -54,16 +54,14 @@ class PostgresTable(DBTable): def create_indexes(self): create_index_query = "" - for key, col in self.columns.items(): + for col in self.columns.values(): if ( col.set_index and col.fieldtype in frappe.db.type_map and frappe.db.type_map.get(col.fieldtype)[0] not in ("text", "longtext") ): create_index_query += ( - 'CREATE INDEX IF NOT EXISTS "{index_name}" ON `{table_name}`(`{field}`);'.format( - index_name=col.fieldname, table_name=self.table_name, field=col.fieldname - ) + f'CREATE INDEX IF NOT EXISTS "{col.fieldname}" ON `{self.table_name}`(`{col.fieldname}`);' ) if create_index_query: # nosemgrep @@ -115,9 +113,7 @@ class PostgresTable(DBTable): for col in self.add_index: # if index key not exists create_contraint_query += ( - 'CREATE INDEX IF NOT EXISTS "{index_name}" ON `{table_name}`(`{field}`);'.format( - index_name=col.fieldname, table_name=self.table_name, field=col.fieldname - ) + f'CREATE INDEX IF NOT EXISTS "{col.fieldname}" ON `{self.table_name}`(`{col.fieldname}`);' ) for col in self.add_unique: diff --git a/frappe/database/query.py b/frappe/database/query.py index bded4e3aee..a94f07ac06 100644 --- a/frappe/database/query.py +++ b/frappe/database/query.py @@ -99,7 +99,7 @@ class Engine: # add fields self.fields = self.parse_fields(fields) if not self.fields: - self.fields = [getattr(self.table, "name")] + self.fields = [self.table.name] self.query._child_queries = [] for field in self.fields: @@ -117,7 +117,7 @@ class Engine: if filters is None: return - if isinstance(filters, (str, int)): + if isinstance(filters, str | int): filters = {"name": str(filters)} if isinstance(filters, Criterion): @@ -126,14 +126,14 @@ class Engine: elif isinstance(filters, dict): self.apply_dict_filters(filters) - elif isinstance(filters, (list, tuple)): - if all(isinstance(d, (str, int)) for d in filters) and len(filters) > 0: + elif isinstance(filters, list | tuple): + if all(isinstance(d, str | int) for d in filters) and len(filters) > 0: self.apply_dict_filters({"name": ("in", filters)}) else: for filter in filters: - if isinstance(filter, (str, int, Criterion, dict)): + if isinstance(filter, str | int | Criterion | dict): self.apply_filters(filter) - elif isinstance(filter, (list, tuple)): + elif isinstance(filter, list | tuple): self.apply_list_filters(filter) def apply_list_filters(self, filter: list): @@ -150,7 +150,7 @@ class Engine: def apply_dict_filters(self, filters: dict[str, str | int | list]): for field, value in filters.items(): operator = "=" - if isinstance(value, (list, tuple)): + if isinstance(value, list | tuple): operator, value = value self._apply_filter(field, value, operator) @@ -187,7 +187,7 @@ class Engine: if isinstance(_value, bool): _value = int(_value) - elif not _value and isinstance(_value, (list, tuple)): + elif not _value and isinstance(_value, list | tuple): _value = ("",) # Nested set @@ -279,7 +279,7 @@ class Engine: return MARIADB_SPECIFIC_COMMENT.sub("", stripped_field) return stripped_field - if isinstance(fields, (list, tuple)): + if isinstance(fields, list | tuple): return [_sanitize_field(field) for field in fields] elif isinstance(fields, str): return _sanitize_field(fields) @@ -304,10 +304,10 @@ class Engine: if not fields: return [] fields = self.sanitize_fields(fields) - if isinstance(fields, (list, tuple, set)) and None in fields and Field not in fields: + if isinstance(fields, list | tuple | set) and None in fields and Field not in fields: return [] - if not isinstance(fields, (list, tuple)): + if not isinstance(fields, list | tuple): fields = [fields] def parse_field(field: str): diff --git a/frappe/database/schema.py b/frappe/database/schema.py index 6994fa88c8..c336ab5ba2 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -144,9 +144,7 @@ class DBTable: try: # check for truncation max_length = frappe.db.sql( - """SELECT MAX(CHAR_LENGTH(`{fieldname}`)) FROM `tab{doctype}`""".format( - fieldname=col.fieldname, doctype=self.doctype - ) + f"""SELECT MAX(CHAR_LENGTH(`{col.fieldname}`)) FROM `tab{self.doctype}`""" ) except frappe.db.InternalError as e: diff --git a/frappe/defaults.py b/frappe/defaults.py index d8ba0dc93b..af95c273a1 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -21,7 +21,7 @@ def get_user_default(key, user=None): d = user_defaults.get(key, None) if is_a_user_permission_key(key): - if d and isinstance(d, (list, tuple)) and len(d) == 1: + if d and isinstance(d, list | tuple) and len(d) == 1: # Use User Permission value when only when it has a single value d = d[0] else: @@ -31,7 +31,7 @@ def get_user_default(key, user=None): # If no default value is found, use the User Permission value d = user_permission_default - value = isinstance(d, (list, tuple)) and d[0] or d + value = isinstance(d, list | tuple) and d[0] or d if not_in_user_permission(key, value, user): return @@ -61,14 +61,14 @@ def get_user_default_as_list(key, user=None): d = user_defaults.get(key, None) if is_a_user_permission_key(key): - if d and isinstance(d, (list, tuple)) and len(d) == 1: + if d and isinstance(d, list | tuple) and len(d) == 1: # Use User Permission value when only when it has a single value d = [d[0]] else: d = user_defaults.get(frappe.scrub(key), None) - d = list(filter(None, (not isinstance(d, (list, tuple))) and [d] or d)) + d = list(filter(None, (not isinstance(d, list | tuple)) and [d] or d)) # filter default values if not found in user permission return [value for value in d if not not_in_user_permission(key, value)] @@ -135,7 +135,7 @@ def add_global_default(key, value): def get_global_default(key): d = get_defaults().get(key, None) - value = isinstance(d, (list, tuple)) and d[0] or d + value = isinstance(d, list | tuple) and d[0] or d if not_in_user_permission(key, value): return diff --git a/frappe/deferred_insert.py b/frappe/deferred_insert.py index 5c7e7a7f0d..fd370d8ac5 100644 --- a/frappe/deferred_insert.py +++ b/frappe/deferred_insert.py @@ -13,7 +13,7 @@ queue_prefix = "insert_queue_for_" def deferred_insert(doctype: str, records: list[Union[dict, "Document"]] | str): - if isinstance(records, (dict, list)): + if isinstance(records, dict | list): _records = json.dumps(records) else: _records = records diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index 26f69be4d2..26968f8430 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -193,6 +193,9 @@ class Workspace: item["count"] = count + if item.get("link_type") == "DocType": + item["description"] = frappe.get_meta(item.link_to).description + # Translate label item["label"] = _(item.label) if item.label else _(item.name) @@ -556,11 +559,11 @@ def save_new_widget(doc, page, blocks, new_widgets): json_config = widgets and dumps(widgets, sort_keys=True, indent=4) # Error log body - log = """ - page: {} - config: {} - exception: {} - """.format(page, json_config, e) + log = f""" + page: {page} + config: {json_config} + exception: {e} + """ doc.log_error("Could not save customization", log) raise diff --git a/frappe/desk/doctype/dashboard/dashboard.py b/frappe/desk/doctype/dashboard/dashboard.py index 0c0a96adba..59efa6e873 100644 --- a/frappe/desk/doctype/dashboard/dashboard.py +++ b/frappe/desk/doctype/dashboard/dashboard.py @@ -124,9 +124,7 @@ def get_non_standard_warning_message(non_standard_docs_map): def get_html(docs, doctype): html = f"

{frappe.bold(doctype)}

" for doc in docs: - html += ''.format( - doctype=doctype, doc=doc - ) + html += f'' html += "
" return html diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py index a4345f269f..318f139a33 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py @@ -55,7 +55,7 @@ def get_permission_query_conditions(user): module_condition = """`tabDashboard Chart`.`module` in ({allowed_modules}) or `tabDashboard Chart`.`module` is NULL""".format(allowed_modules=",".join(allowed_modules)) - return """ + return f""" ((`tabDashboard Chart`.`chart_type` in ('Count', 'Sum', 'Average') and {doctype_condition}) or @@ -63,11 +63,7 @@ def get_permission_query_conditions(user): and {report_condition})) and ({module_condition}) - """.format( - doctype_condition=doctype_condition, - report_condition=report_condition, - module_condition=module_condition, - ) + """ def has_permission(doc, ptype, user): @@ -248,9 +244,7 @@ def get_heatmap_chart_config(chart, filters, heatmap_year): doctype, fields=[ timestamp_field, - "{aggregate_function}({value_field})".format( - aggregate_function=aggregate_function, value_field=value_field - ), + f"{aggregate_function}({value_field})", ], filters=filters, group_by=f"date({datefield})", @@ -307,7 +301,7 @@ def get_result(data, timegrain, from_date, to_date, chart_type): result = [[date, 0] for date in dates] data_index = 0 if data: - for i, d in enumerate(result): + for d in result: count = 0 while data_index < len(data) and getdate(data[data_index][0]) <= d[0]: d[1] += data[data_index][1] diff --git a/frappe/desk/doctype/event/event.py b/frappe/desk/doctype/event/event.py index c35e4ef136..0765ad9f50 100644 --- a/frappe/desk/doctype/event/event.py +++ b/frappe/desk/doctype/event/event.py @@ -221,9 +221,7 @@ def delete_communication(event, reference_doctype, reference_docname): def get_permission_query_conditions(user): if not user: user = frappe.session.user - return """(`tabEvent`.`event_type`='Public' or `tabEvent`.`owner`={user})""".format( - user=frappe.db.escape(user), - ) + return f"""(`tabEvent`.`event_type`='Public' or `tabEvent`.`owner`={frappe.db.escape(user)})""" def has_permission(doc, user): diff --git a/frappe/desk/doctype/kanban_board/kanban_board.py b/frappe/desk/doctype/kanban_board/kanban_board.py index c3a96c3c42..c8d26acb03 100644 --- a/frappe/desk/doctype/kanban_board/kanban_board.py +++ b/frappe/desk/doctype/kanban_board/kanban_board.py @@ -52,9 +52,7 @@ def get_permission_query_conditions(user): if user == "Administrator": return "" - return """(`tabKanban Board`.private=0 or `tabKanban Board`.owner={user})""".format( - user=frappe.db.escape(user) - ) + return f"""(`tabKanban Board`.private=0 or `tabKanban Board`.owner={frappe.db.escape(user)})""" def has_permission(doc, ptype, user): diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py index 034dff7f68..aea6743ce1 100644 --- a/frappe/desk/doctype/number_card/number_card.py +++ b/frappe/desk/doctype/number_card/number_card.py @@ -101,11 +101,11 @@ def get_permission_query_conditions(user=None): module_condition = """`tabNumber Card`.`module` in ({allowed_modules}) or `tabNumber Card`.`module` is NULL""".format(allowed_modules=",".join(allowed_modules)) - return """ + return f""" {doctype_condition} and {module_condition} - """.format(doctype_condition=doctype_condition, module_condition=module_condition) + """ def has_permission(doc, ptype, user): @@ -141,11 +141,7 @@ def get_result(doc, filters, to_date=None): if function == "count": fields = [f"{function}(*) as result"] else: - fields = [ - "{function}({based_on}) as result".format( - function=function, based_on=doc.aggregate_function_based_on - ) - ] + fields = [f"{function}({doc.aggregate_function_based_on}) as result"] if not filters: filters = [] diff --git a/frappe/desk/form/linked_with.py b/frappe/desk/form/linked_with.py index c51186a493..23300b0a1c 100644 --- a/frappe/desk/form/linked_with.py +++ b/frappe/desk/form/linked_with.py @@ -214,7 +214,7 @@ def get_references_across_doctypes( for k, v in references_by_dlink_fields.items(): references.setdefault(k, []).extend(v) - for doctype, links in references.items(): + for links in references.values(): for link in links: link["is_child"] = link["doctype"] in all_child_tables return references diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index e090cf921e..d3c448fa03 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -286,9 +286,9 @@ def get_communication_data( conditions = "" if after: # find after a particular date - conditions += """ - AND C.creation > {} - """.format(after) + conditions += f""" + AND C.creation > {after} + """ if doctype == "User": conditions += """ @@ -296,23 +296,23 @@ def get_communication_data( """ # communications linked to reference_doctype - part1 = """ + part1 = f""" SELECT {fields} FROM `tabCommunication` as C WHERE C.communication_type IN ('Communication', 'Feedback', 'Automated Message') AND (C.reference_doctype = %(doctype)s AND C.reference_name = %(name)s) {conditions} - """.format(fields=fields, conditions=conditions) + """ # communications linked in Timeline Links - part2 = """ + part2 = f""" SELECT {fields} FROM `tabCommunication` as C INNER JOIN `tabCommunication Link` ON C.name=`tabCommunication Link`.parent WHERE C.communication_type IN ('Communication', 'Feedback', 'Automated Message') AND `tabCommunication Link`.link_doctype = %(doctype)s AND `tabCommunication Link`.link_name = %(name)s {conditions} - """.format(fields=fields, conditions=conditions) + """ return frappe.db.sql( """ diff --git a/frappe/desk/form/utils.py b/frappe/desk/form/utils.py index 353dea305f..b93ebdb5bc 100644 --- a/frappe/desk/form/utils.py +++ b/frappe/desk/form/utils.py @@ -105,6 +105,4 @@ def get_next(doctype, value, prev, filters=None, sort_order="desc", sort_field=" def get_pdf_link(doctype, docname, print_format="Standard", no_letterhead=0): - return "/api/method/frappe.utils.print_format.download_pdf?doctype={doctype}&name={docname}&format={print_format}&no_letterhead={no_letterhead}".format( - doctype=doctype, docname=docname, print_format=print_format, no_letterhead=no_letterhead - ) + return f"/api/method/frappe.utils.print_format.download_pdf?doctype={doctype}&name={docname}&format={print_format}&no_letterhead={no_letterhead}" diff --git a/frappe/desk/leaderboard.py b/frappe/desk/leaderboard.py index 91aa2084cc..4800a7d945 100644 --- a/frappe/desk/leaderboard.py +++ b/frappe/desk/leaderboard.py @@ -45,8 +45,6 @@ def get_energy_point_leaderboard(date_range, company=None, field=None, limit=Non for user in energy_point_users: user_id = user["name"] user["name"] = get_fullname(user["name"]) - user["formatted_name"] = '{}'.format( - user_id, get_fullname(user_id) - ) + user["formatted_name"] = f'{get_fullname(user_id)}' return energy_point_users diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py index 8132979dff..3ec0338e83 100644 --- a/frappe/desk/notifications.py +++ b/frappe/desk/notifications.py @@ -283,7 +283,7 @@ def get_open_count(doctype, name, items=None): try: external_links_data_for_d = get_external_links(d, name, links) out["external_links_found"].append(external_links_data_for_d) - except Exception as e: + except Exception: out["external_links_found"].append({"doctype": d, "open_count": 0, "count": 0}) else: external_links_data_for_d = get_external_links(d, name, links) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index d4189529aa..3003573d81 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -124,7 +124,7 @@ def normalize_result(result, columns): # Convert to list of dicts from list of lists/tuples data = [] column_names = [column["fieldname"] for column in columns] - if result and isinstance(result[0], (list, tuple)): + if result and isinstance(result[0], list | tuple): for row in result: row_obj = {} for idx, column_name in enumerate(column_names): @@ -507,7 +507,7 @@ def get_data_for_custom_field(doctype, field, names=None): filters = {} if names: - if isinstance(names, (str, bytearray)): + if isinstance(names, str | bytearray): names = frappe.json.loads(names) filters.update({"name": ["in", names]}) @@ -659,7 +659,7 @@ def has_match( cell_value = None if isinstance(row, dict): cell_value = row.get(idx) - elif isinstance(row, (list, tuple)): + elif isinstance(row, list | tuple): cell_value = row[idx] if ( @@ -691,10 +691,10 @@ def get_linked_doctypes(columns, data): columns_dict = get_columns_dict(columns) - for idx, col in enumerate(columns): + for idx in range(len(columns)): df = columns_dict[idx] if df.get("fieldtype") == "Link": - if data and isinstance(data[0], (list, tuple)): + if data and isinstance(data[0], list | tuple): linked_doctypes[df["options"]] = idx else: # dict @@ -705,7 +705,7 @@ def get_linked_doctypes(columns, data): for row in data: if row: if len(row) != len(columns_with_value): - if isinstance(row, (list, tuple)): + if isinstance(row, list | tuple): row = enumerate(row) elif isinstance(row, dict): row = row.items() diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index c69542c44c..ae0d915b7c 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -199,7 +199,7 @@ def get_meta_and_docfield(fieldname, data): def update_wildcard_field_param(data): if (isinstance(data.fields, str) and data.fields == "*") or ( - isinstance(data.fields, (list, tuple)) and len(data.fields) == 1 and data.fields[0] == "*" + isinstance(data.fields, list | tuple) and len(data.fields) == 1 and data.fields[0] == "*" ): data.fields = get_permitted_fields(data.doctype, parenttype=data.parenttype) return True @@ -390,10 +390,10 @@ def append_totals_row(data): for row in data: for i in range(len(row)): - if isinstance(row[i], (float, int)): + if isinstance(row[i], float | int): totals[i] = (totals[i] or 0) + row[i] - if not isinstance(totals[0], (int, float)): + if not isinstance(totals[0], int | float): totals[0] = "Total" data.append(totals) @@ -568,7 +568,7 @@ def get_stats(stats, doctype, filters=None): except frappe.db.SQLError: pass - except frappe.db.InternalError as e: + except frappe.db.InternalError: # raised when _user_tags column is added on the fly pass @@ -673,7 +673,7 @@ def get_filters_cond(doctype, filters, conditions, ignore_permissions=None, with for f in filters: if isinstance(f[1], str) and f[1][0] == "!": flt.append([doctype, f[0], "!=", f[1][1:]]) - elif isinstance(f[1], (list, tuple)) and f[1][0].lower() in ( + elif isinstance(f[1], list | tuple) and f[1][0].lower() in ( "=", ">", "<", diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index 88cd216e9a..9ec44ab383 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -321,7 +321,7 @@ def send_daily(): continue try: auto_email_report.send() - except Exception as e: + except Exception: auto_email_report.log_error(f"Failed to send {auto_email_report.name} Auto Email Report") diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index a438af445d..3fa72d4680 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -4,7 +4,6 @@ import email.utils import functools import imaplib -import socket import time from datetime import datetime, timedelta from poplib import error_proto @@ -96,7 +95,15 @@ class EmailAccount(Document): password: DF.Password | None send_notification_to: DF.SmallText | None send_unsubscribe_message: DF.Check - service: DF.Literal["", "GMail", "Sendgrid", "SparkPost", "Yahoo Mail", "Outlook.com", "Yandex.Mail"] + service: DF.Literal[ + "", + "GMail", + "Sendgrid", + "SparkPost", + "Yahoo Mail", + "Outlook.com", + "Yandex.Mail", + ] signature: DF.TextEditor | None smtp_port: DF.Data | None smtp_server: DF.Data | None @@ -191,20 +198,27 @@ class EmailAccount(Document): self.default_incoming = False messages.append( _("{} has been disabled. It can only be enabled if {} is checked.").format( - frappe.bold(_("Default Incoming")), frappe.bold(_("Enable Incoming")) + frappe.bold(_("Default Incoming")), + frappe.bold(_("Enable Incoming")), ) ) if not self.enable_outgoing and self.default_outgoing: self.default_outgoing = False messages.append( _("{} has been disabled. It can only be enabled if {} is checked.").format( - frappe.bold(_("Default Outgoing")), frappe.bold(_("Enable Outgoing")) + frappe.bold(_("Default Outgoing")), + frappe.bold(_("Enable Outgoing")), ) ) if messages: if len(messages) == 1: (as_list, messages) = (0, messages[0]) - frappe.msgprint(messages, as_list=as_list, indicator="orange", title=_("Defaults Updated")) + frappe.msgprint( + messages, + as_list=as_list, + indicator="orange", + title=_("Defaults Updated"), + ) def on_update(self): """Check there is only one default of each type.""" @@ -284,7 +298,11 @@ class EmailAccount(Document): "loginfailed", ] - other_error_codes = ["err[auth]", "errtemporaryerror", "loginviayourwebbrowser"] + other_error_codes = [ + "err[auth]", + "errtemporaryerror", + "loginviayourwebbrowser", + ] all_error_codes = auth_error_codes + other_error_codes @@ -563,7 +581,15 @@ class EmailAccount(Document): seen_status = messages.get("seen_status", {}).get(uid) if self.email_sync_option != "UNSEEN" or seen_status != "SEEN": # only append the emails with status != 'SEEN' if sync option is set to 'UNSEEN' - mails.append(InboundMail(message, self, frappe.safe_decode(uid), seen_status, append_to)) + mails.append( + InboundMail( + message, + self, + frappe.safe_decode(uid), + seen_status, + append_to, + ) + ) if not self.enable_incoming: return [] @@ -618,7 +644,9 @@ class EmailAccount(Document): def send_auto_reply(self, communication, email): """Send auto reply if set.""" - from frappe.core.doctype.communication.email import set_incoming_outgoing_accounts + from frappe.core.doctype.communication.email import ( + set_incoming_outgoing_accounts, + ) if self.enable_auto_reply: set_incoming_outgoing_accounts(communication) @@ -671,7 +699,10 @@ class EmailAccount(Document): if not self.enable_incoming: frappe.throw(_("Automatic Linking can be activated only if Incoming is enabled.")) - if frappe.db.exists("Email Account", {"enable_automatic_linking": 1, "name": ("!=", self.name)}): + if frappe.db.exists( + "Email Account", + {"enable_automatic_linking": 1, "name": ("!=", self.name)}, + ): frappe.throw(_("Automatic Linking can be activated only for one Email Account.")) def append_email_to_sent_folder(self, message): @@ -717,7 +748,9 @@ def notify_unreplied(): """Sends email notifications if there are unreplied Communications and `notify_if_unreplied` is set as true.""" for email_account in frappe.get_all( - "Email Account", "name", filters={"enable_incoming": 1, "notify_if_unreplied": 1} + "Email Account", + "name", + filters={"enable_incoming": 1, "notify_if_unreplied": 1}, ): email_account = frappe.get_doc("Email Account", email_account.name) @@ -774,7 +807,12 @@ def pull(now=False): doctype = frappe.qb.DocType("Email Account") email_accounts = ( frappe.qb.from_(doctype) - .select(doctype.name, doctype.auth_method, doctype.connected_app, doctype.connected_user) + .select( + doctype.name, + doctype.auth_method, + doctype.connected_app, + doctype.connected_user, + ) .where(doctype.enable_incoming == 1) .where(doctype.awaiting_password == 0) .run(as_dict=1) @@ -820,10 +858,9 @@ def pull_from_email_account(email_account): def get_max_email_uid(email_account): - # get maximum uid of emails - max_uid = 1 + """get maximum uid of emails""" - result = frappe.get_all( + if result := frappe.get_all( "Communication", filters={ "communication_medium": "Email", @@ -831,12 +868,9 @@ def get_max_email_uid(email_account): "email_account": email_account, }, fields=["max(uid) as uid"], - ) - - if not result: - return 1 - else: + ): return cint(result[0].get("uid", 0)) + 1 + return 1 def setup_user_email_inbox(email_account, awaiting_password, email_id, enable_outgoing, used_oauth): @@ -868,7 +902,11 @@ def setup_user_email_inbox(email_account, awaiting_password, email_id, enable_ou # check if inbox is alreay configured user_inbox = ( - frappe.db.get_value("User Email", {"email_account": email_account, "parent": user_name}, ["name"]) + frappe.db.get_value( + "User Email", + {"email_account": email_account, "parent": user_name}, + ["name"], + ) or None ) @@ -895,7 +933,11 @@ def remove_user_email_inbox(email_account): if not email_account: return - users = frappe.get_all("User Email", filters={"email_account": email_account}, fields=["parent as name"]) + users = frappe.get_all( + "User Email", + filters={"email_account": email_account}, + fields=["parent as name"], + ) for user in users: doc = frappe.get_doc("User", user.get("name")) diff --git a/frappe/email/doctype/email_account/test_email_account.py b/frappe/email/doctype/email_account/test_email_account.py index 16364104fa..4728881d7e 100644 --- a/frappe/email/doctype/email_account/test_email_account.py +++ b/frappe/email/doctype/email_account/test_email_account.py @@ -413,10 +413,13 @@ class TestEmailAccount(FrappeTestCase): @patch("frappe.email.receive.EmailServer.select_imap_folder", return_value=True) @patch("frappe.email.receive.EmailServer.logout", side_effect=lambda: None) def mocked_get_inbound_mails( - email_account, messages={}, mocked_logout=None, mocked_select_imap_folder=None + email_account, messages=None, mocked_logout=None, mocked_select_imap_folder=None ): from frappe.email.receive import EmailServer + if messages is None: + messages = {} + def get_mocked_messages(**kwargs): return messages.get(kwargs["folder"], {}) @@ -427,7 +430,12 @@ class TestEmailAccount(FrappeTestCase): @patch("frappe.email.receive.EmailServer.select_imap_folder", return_value=True) @patch("frappe.email.receive.EmailServer.logout", side_effect=lambda: None) - def mocked_email_receive(email_account, messages={}, mocked_logout=None, mocked_select_imap_folder=None): + def mocked_email_receive( + email_account, messages=None, mocked_logout=None, mocked_select_imap_folder=None + ): + if messages is None: + messages = {} + def get_mocked_messages(**kwargs): return messages.get(kwargs["folder"], {}) diff --git a/frappe/email/doctype/email_group/email_group.py b/frappe/email/doctype/email_group/email_group.py index a6fafe9a03..8c80a52fcb 100755 --- a/frappe/email/doctype/email_group/email_group.py +++ b/frappe/email/doctype/email_group/email_group.py @@ -105,7 +105,7 @@ def import_from(name, doctype): @frappe.whitelist() def add_subscribers(name, email_list): - if not isinstance(email_list, (list, tuple)): + if not isinstance(email_list, list | tuple): email_list = email_list.replace(",", "\n").split("\n") template = frappe.db.get_value("Email Group", name, "welcome_email_template") diff --git a/frappe/frappeclient.py b/frappe/frappeclient.py index 00d0ef332a..1bee9db571 100644 --- a/frappe/frappeclient.py +++ b/frappe/frappeclient.py @@ -356,7 +356,7 @@ class FrappeClient: def preprocess(self, params): """convert dicts, lists to json""" for key, value in params.items(): - if isinstance(value, (dict, list)): + if isinstance(value, dict | list): params[key] = json.dumps(value) return params diff --git a/frappe/gettext/extractors/navbar.py b/frappe/gettext/extractors/navbar.py index c40790dd41..924d3a4403 100644 --- a/frappe/gettext/extractors/navbar.py +++ b/frappe/gettext/extractors/navbar.py @@ -14,7 +14,7 @@ def extract(fileobj, *args, **kwargs): module = get_module(fileobj.name) if hasattr(module, "standard_navbar_items"): - standard_navbar_items = getattr(module, "standard_navbar_items") + standard_navbar_items = module.standard_navbar_items for nav_item in standard_navbar_items: if label := nav_item.get("item_label"): item_type = nav_item.get("item_type") @@ -29,7 +29,7 @@ def extract(fileobj, *args, **kwargs): ) if hasattr(module, "standard_help_items"): - standard_help_items = getattr(module, "standard_help_items") + standard_help_items = module.standard_help_items for help_item in standard_help_items: if label := help_item.get("item_label"): item_type = nav_item.get("item_type") diff --git a/frappe/installer.py b/frappe/installer.py index d33926690d..682ef4fd7c 100644 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -834,10 +834,10 @@ def partial_restore(sql_file_path, verbose=False): warn = click.style( "Delete the tables you want to restore manually before attempting" - " partial restore operation for PostreSQL databases", + " partial restore operation for PostgreSQL databases", fg="yellow", ) - warnings.warn(warn) + warnings.warn(warn, stacklevel=2) else: click.secho("Unsupported database type", fg="red") return diff --git a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py index 031ba18817..edcbba1c3f 100644 --- a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py +++ b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py @@ -106,7 +106,7 @@ def take_backup_to_dropbox(retry_count=0, upload_db_backup=True): if isinstance(error_log, str): error_message = error_log + "\n" + frappe.get_traceback() else: - file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)] + file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log, strict=False)] error_message = "\n".join(file_and_error) + "\n" + frappe.get_traceback() send_email(False, "Dropbox", "Dropbox Settings", "send_notifications_to", error_message) diff --git a/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py b/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py index 1fcda3e077..92918e8197 100644 --- a/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py @@ -489,7 +489,7 @@ class LDAP_TestCase: @mock_ldap_connection def test_get_ldap_attributes(self): method_return = self.test_class.get_ldap_attributes() - self.assertTrue(type(method_return) is list) + self.assertTrue(isinstance(method_return, list)) @mock_ldap_connection def test_fetch_ldap_groups(self): @@ -599,7 +599,7 @@ class LDAP_TestCase: test_ldap_entry = self.connection.entries[0] method_return = self.test_class.convert_ldap_entry_to_dict(test_ldap_entry) - self.assertTrue(type(method_return) is dict) # must be dict + self.assertTrue(isinstance(method_return, dict)) # must be dict self.assertTrue(len(method_return) == 6) # there are 6 fields in mock_ldap for use diff --git a/frappe/integrations/frappe_providers/frappecloud.py b/frappe/integrations/frappe_providers/frappecloud.py index bae811d41d..f276004427 100644 --- a/frappe/integrations/frappe_providers/frappecloud.py +++ b/frappe/integrations/frappe_providers/frappecloud.py @@ -12,11 +12,7 @@ def frappecloud_migrator(local_site): request = requests.get(request_url) if request.status_code / 100 != 2: - print( - "Request exitted with Status Code: {}\nPayload: {}".format( - request.status_code, html2text(request.text) - ) - ) + print(f"Request exited with Status Code: {request.status_code}\nPayload: {html2text(request.text)}") click.secho( "Some errors occurred while recovering the migration script. Please contact us @ Frappe Cloud if this issue persists", fg="yellow", diff --git a/frappe/integrations/google_oauth.py b/frappe/integrations/google_oauth.py index 19d6035f3a..c3a154f0e9 100644 --- a/frappe/integrations/google_oauth.py +++ b/frappe/integrations/google_oauth.py @@ -39,7 +39,7 @@ class GoogleOAuth: self.domain = domain.lower() self.scopes = ( " ".join(_SCOPES[self.domain]) - if isinstance(_SCOPES[self.domain], (list, tuple)) + if isinstance(_SCOPES[self.domain], list | tuple) else _SCOPES[self.domain] ) diff --git a/frappe/integrations/offsite_backup_utils.py b/frappe/integrations/offsite_backup_utils.py index da9f35b146..8cd29d5ecc 100644 --- a/frappe/integrations/offsite_backup_utils.py +++ b/frappe/integrations/offsite_backup_utils.py @@ -29,11 +29,11 @@ def send_email(success, service_name, doctype, email_field, error_status=None): ) else: subject = "[Warning] Backup Upload Failed" - message = """ + message = f"""

Backup Upload Failed!

-

Oops, your automated backup to {} failed.

-

Error message: {}

-

Please contact your system manager for more information.

""".format(service_name, error_status) +

Oops, your automated backup to {service_name} failed.

+

Error message: {error_status}

+

Please contact your system manager for more information.

""" frappe.sendmail(recipients=recipients, subject=subject, message=message) diff --git a/frappe/integrations/utils.py b/frappe/integrations/utils.py index ca6065c577..47fec33013 100644 --- a/frappe/integrations/utils.py +++ b/frappe/integrations/utils.py @@ -155,5 +155,5 @@ def get_json(obj): def json_handler(obj): - if isinstance(obj, (datetime.date, datetime.timedelta, datetime.datetime)): + if isinstance(obj, datetime.date | datetime.timedelta | datetime.datetime): return str(obj) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 2c7933b1d7..943fcfae10 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -217,7 +217,7 @@ class BaseDocument: value = self.__dict__.get(key, default) - if limit and isinstance(value, (list, tuple)) and len(value) > limit: + if limit and isinstance(value, list | tuple) and len(value) > limit: value = value[:limit] return value @@ -396,7 +396,7 @@ class BaseDocument: value = None if convert_dates_to_str and isinstance( - value, (datetime.datetime, datetime.date, datetime.time, datetime.timedelta) + value, datetime.datetime | datetime.date | datetime.time | datetime.timedelta ): value = str(value) @@ -1189,7 +1189,7 @@ class BaseDocument: if not doc: doc = getattr(self, "parent_doc", None) or self - if (absolute_value or doc.get("absolute_value")) and isinstance(val, (int, float)): + if (absolute_value or doc.get("absolute_value")) and isinstance(val, int | float): val = abs(self.get(fieldname)) return format_value(val, df=df, doc=doc, currency=currency, format=format) @@ -1297,7 +1297,7 @@ def _filter(data, filters, limit=None): for f in filters: fval = filters[f] - if not isinstance(fval, (tuple, list)): + if not isinstance(fval, tuple | list): if fval is True: fval = ("not None", fval) elif fval is False: diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 23a46425c5..da4dd4f112 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -222,15 +222,12 @@ class DatabaseQuery: if frappe.db.db_type == "postgres" and args.order_by and args.group_by: args = self.prepare_select_args(args) - query = ( - """select %(fields)s - from %(tables)s - %(conditions)s - %(group_by)s - %(order_by)s - %(limit)s""" - % args - ) + query = """select {fields} + from {tables} + {conditions} + {group_by} + {order_by} + {limit}""".format(**args) return frappe.db.sql( query, @@ -1270,7 +1267,7 @@ def get_between_date_filter(value, df=None): from_date = frappe.utils.nowdate() to_date = frappe.utils.nowdate() - if value and isinstance(value, (list, tuple)): + if value and isinstance(value, list | tuple): if len(value) >= 1: from_date = value[0] if len(value) >= 2: diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 5967e9c5c9..c6cce21a06 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -354,8 +354,8 @@ def check_if_doc_is_dynamically_linked(doc, method="Delete"): def raise_link_exists_exception(doc, reference_doctype, reference_docname, row=""): - doc_link = '{1}'.format(doc.doctype, doc.name) - reference_link = '{1}'.format(reference_doctype, reference_docname) + doc_link = f'{doc.name}' + reference_link = f'{reference_docname}' # hack to display Single doctype only once in message if reference_doctype == reference_docname: @@ -407,12 +407,12 @@ def clear_references( reference_name_field="reference_name", ): frappe.db.sql( - """update - `tab{0}` + f"""update + `tab{doctype}` set - {1}=NULL, {2}=NULL + {reference_doctype_field}=NULL, {reference_name_field}=NULL where - {1}=%s and {2}=%s""".format(doctype, reference_doctype_field, reference_name_field), # nosec + {reference_doctype_field}=%s and {reference_name_field}=%s""", # nosec (reference_doctype, reference_name), ) diff --git a/frappe/model/document.py b/frappe/model/document.py index 48c37bd8e5..090008492e 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -161,7 +161,7 @@ class Document(BaseDocument): else: get_value_kwargs = {"for_update": self.flags.for_update, "as_dict": True} - if not isinstance(self.name, (dict, list)): + if not isinstance(self.name, dict | list): get_value_kwargs["order_by"] = None d = frappe.db.get_value( @@ -883,7 +883,7 @@ class Document(BaseDocument): if not missing: return - for fieldname, msg in missing: + for idx, msg in missing: # noqa: B007 msgprint(msg) if frappe.flags.print_messages: @@ -1252,7 +1252,7 @@ class Document(BaseDocument): doc_to_compare = frappe.get_doc(self.doctype, amended_from) version = frappe.new_doc("Version") - if is_useful_diff := version.update_version_info(doc_to_compare, self): + if version.update_version_info(doc_to_compare, self): version.insert(ignore_permissions=True) if not frappe.flags.in_migrate: @@ -1540,7 +1540,7 @@ class Document(BaseDocument): file_lock.delete_lock(signature) lock_exists = False if timeout: - for i in range(timeout): + for _ in range(timeout): time.sleep(1) if not file_lock.lock_exists(signature): lock_exists = False diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 7a205cf507..8dca6b32bf 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -147,7 +147,7 @@ class Meta(Document): def serialize(doc): out = {} for key, value in doc.__dict__.items(): - if isinstance(value, (list, tuple)): + if isinstance(value, list | tuple): if not value or not isinstance(value[0], BaseDocument): # non standard list object, skip continue @@ -155,7 +155,7 @@ class Meta(Document): value = [serialize(d) for d in value] if (not no_nulls and value is None) or isinstance( - value, (str, int, float, datetime, list, tuple) + value, str | int | float | datetime | list | tuple ): out[key] = value @@ -713,9 +713,7 @@ class Meta(Document): module_name, "doctype", doctype, "templates", doctype + suffix + ".html" ) if os.path.exists(template_path): - return "{module_name}/doctype/{doctype_name}/templates/{doctype_name}{suffix}.html".format( - module_name=module_name, doctype_name=doctype, suffix=suffix - ) + return f"{module_name}/doctype/{doctype}/templates/{doctype}{suffix}.html" return None def is_nested_set(self): diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 5a0e23159f..15e4c549e6 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -465,12 +465,10 @@ def append_number_if_name_exists(doctype, value, fieldname="name", separator="-" if exists: last = frappe.db.sql( - """SELECT `{fieldname}` FROM `tab{doctype}` - WHERE `{fieldname}` {regex_character} %s + f"""SELECT `{fieldname}` FROM `tab{doctype}` + WHERE `{fieldname}` {frappe.db.REGEX_CHARACTER} %s ORDER BY length({fieldname}) DESC, - `{fieldname}` DESC LIMIT 1""".format( - doctype=doctype, fieldname=fieldname, regex_character=frappe.db.REGEX_CHARACTER - ), + `{fieldname}` DESC LIMIT 1""", regex, ) diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 42864880a0..b535331c23 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -47,7 +47,7 @@ def update_document_title( # TODO: omit this after runtime type checking (ref: https://github.com/frappe/frappe/pull/14927) for obj in [docname, updated_title, updated_name]: - if not isinstance(obj, (str, NoneType)): + if not isinstance(obj, str | NoneType): frappe.throw(f"{obj=} must be of type str or None") # handle bad API usages diff --git a/frappe/model/utils/__init__.py b/frappe/model/utils/__init__.py index d0015101b1..7d8a389ec9 100644 --- a/frappe/model/utils/__init__.py +++ b/frappe/model/utils/__init__.py @@ -3,7 +3,6 @@ import re import frappe -from frappe import _ from frappe.build import html_to_js_template from frappe.utils import cstr from frappe.utils.caching import site_cache @@ -30,9 +29,8 @@ def set_default(doc, key): frappe.db.set(doc, "is_default", 1) frappe.db.sql( - """update `tab%s` set `is_default`=0 - where `%s`=%s and name!=%s""" - % (doc.doctype, key, "%s", "%s"), + """update `tab{}` set `is_default`=0 + where `{}`={} and name!={}""".format(doc.doctype, key, "%s", "%s"), (doc.get(key), doc.name), ) @@ -62,7 +60,7 @@ def render_include(content): content = cstr(content) # try 5 levels of includes - for i in range(5): + for _ in range(5): if "{% include" in content: paths = INCLUDE_DIRECTIVE_PATTERN.findall(content) if not paths: diff --git a/frappe/model/utils/rename_field.py b/frappe/model/utils/rename_field.py index e3b0835ddd..23c934da74 100644 --- a/frappe/model/utils/rename_field.py +++ b/frappe/model/utils/rename_field.py @@ -27,9 +27,8 @@ def rename_field(doctype, old_fieldname, new_fieldname, validate=True): if new_field.fieldtype in table_fields: # change parentfield of table mentioned in options frappe.db.sql( - """update `tab%s` set parentfield=%s - where parentfield=%s""" - % (new_field.options.split("\n", 1)[0], "%s", "%s"), + """update `tab{}` set parentfield={} + where parentfield={}""".format(new_field.options.split("\n", 1)[0], "%s", "%s"), (new_fieldname, old_fieldname), ) @@ -142,9 +141,8 @@ def update_users_report_view_settings(doctype, ref_fieldname, new_fieldname): if columns_modified: frappe.db.sql( - """update `tabDefaultValue` set defvalue=%s - where defkey=%s""" - % ("%s", "%s"), + """update `tabDefaultValue` set defvalue={} + where defkey={}""".format("%s", "%s"), (json.dumps(new_columns), key), ) diff --git a/frappe/modules/import_file.py b/frappe/modules/import_file.py index 56f3956f6f..52379892d0 100644 --- a/frappe/modules/import_file.py +++ b/frappe/modules/import_file.py @@ -38,7 +38,7 @@ ignore_doctypes = [""] def import_files(module, dt=None, dn=None, force=False, pre_process=None, reset_permissions=False): - if type(module) is list: + if isinstance(module, list): return [ import_file( m[0], @@ -209,11 +209,7 @@ def import_doc( docdict["__islocal"] = 1 controller = get_controller(docdict["doctype"]) - if ( - controller - and hasattr(controller, "prepare_for_import") - and callable(getattr(controller, "prepare_for_import")) - ): + if controller and hasattr(controller, "prepare_for_import") and callable(controller.prepare_for_import): controller.prepare_for_import(docdict) doc = frappe.get_doc(docdict) diff --git a/frappe/parallel_test_runner.py b/frappe/parallel_test_runner.py index 8f101be883..b0d3244e18 100644 --- a/frappe/parallel_test_runner.py +++ b/frappe/parallel_test_runner.py @@ -143,7 +143,7 @@ def split_by_weight(work, weights, chunk_count): chunk_no = 0 chunk_weight = 0 - for task, weight in zip(work, weights): + for task, weight in zip(work, weights, strict=False): if chunk_weight > expected_weight: chunk_weight = 0 chunk_no += 1 diff --git a/frappe/patches/v11_0/delete_duplicate_user_permissions.py b/frappe/patches/v11_0/delete_duplicate_user_permissions.py index a8bb291769..27af1e98c7 100644 --- a/frappe/patches/v11_0/delete_duplicate_user_permissions.py +++ b/frappe/patches/v11_0/delete_duplicate_user_permissions.py @@ -12,7 +12,7 @@ def execute(): for record in duplicateRecords: frappe.db.sql( - """delete from `tabUser Permission` - where allow=%s and user=%s and for_value=%s limit {}""".format(record.count - 1), + f"""delete from `tabUser Permission` + where allow=%s and user=%s and for_value=%s limit {record.count - 1}""", (record.allow, record.user, record.for_value), ) diff --git a/frappe/patches/v11_0/update_list_user_settings.py b/frappe/patches/v11_0/update_list_user_settings.py index 5cbcd3bc0a..f90871af27 100644 --- a/frappe/patches/v11_0/update_list_user_settings.py +++ b/frappe/patches/v11_0/update_list_user_settings.py @@ -12,8 +12,8 @@ def execute(): for user in users: # get user_settings for each user settings = frappe.db.sql( - "select * from `__UserSettings` \ - where user={}".format(frappe.db.escape(user.user)), + f"select * from `__UserSettings` \ + where user={frappe.db.escape(user.user)}", as_dict=True, ) diff --git a/frappe/patches/v12_0/move_email_and_phone_to_child_table.py b/frappe/patches/v12_0/move_email_and_phone_to_child_table.py index 7511a2ee1d..e1fcb72e21 100644 --- a/frappe/patches/v12_0/move_email_and_phone_to_child_table.py +++ b/frappe/patches/v12_0/move_email_and_phone_to_child_table.py @@ -22,7 +22,6 @@ def execute(): phone_values = [] for count, contact_detail in enumerate(contact_details): phone_counter = 1 - is_primary = 1 if contact_detail.email_id: email_values.append( ( diff --git a/frappe/patches/v12_0/replace_null_values_in_tables.py b/frappe/patches/v12_0/replace_null_values_in_tables.py index 1dc8d964a1..617e9886f6 100644 --- a/frappe/patches/v12_0/replace_null_values_in_tables.py +++ b/frappe/patches/v12_0/replace_null_values_in_tables.py @@ -18,7 +18,7 @@ def execute(): update_column_table_map.setdefault(field.TABLE_NAME, []) update_column_table_map[field.TABLE_NAME].append( - "`{fieldname}`=COALESCE(`{fieldname}`, 0)".format(fieldname=field.COLUMN_NAME) + f"`{field.COLUMN_NAME}`=COALESCE(`{field.COLUMN_NAME}`, 0)" ) for table in frappe.db.get_tables(): diff --git a/frappe/patches/v13_0/update_date_filters_in_user_settings.py b/frappe/patches/v13_0/update_date_filters_in_user_settings.py index 6565641d9b..030ea3936d 100644 --- a/frappe/patches/v13_0/update_date_filters_in_user_settings.py +++ b/frappe/patches/v13_0/update_date_filters_in_user_settings.py @@ -9,12 +9,12 @@ def execute(): for user in users: user_settings = frappe.db.sql( - """ + f""" select * from `__UserSettings` where - user='{user}' - """.format(user=user.user), + user='{user.user}' + """, as_dict=True, ) diff --git a/frappe/permissions.py b/frappe/permissions.py index cb13ca45af..97e31255ef 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -127,7 +127,7 @@ def has_permission( meta = frappe.get_meta(doctype) if doc: - if isinstance(doc, (str, int)): + if isinstance(doc, str | int): doc = frappe.get_doc(meta.name, doc) perm = get_doc_permissions(doc, user=user, ptype=ptype, debug=debug).get(ptype) if not perm: diff --git a/frappe/public/js/frappe/utils/diffview.js b/frappe/public/js/frappe/utils/diffview.js index 210e582b7a..7912ffb80d 100644 --- a/frappe/public/js/frappe/utils/diffview.js +++ b/frappe/public/js/frappe/utils/diffview.js @@ -20,6 +20,7 @@ frappe.ui.DiffView = class DiffView { docname: this.docname, ref_doctype: this.doctype, fieldname: this.fieldname, + page_len: 100, }, }); const onchange = () => this.compute_diff(); diff --git a/frappe/public/js/frappe/widgets/links_widget.js b/frappe/public/js/frappe/widgets/links_widget.js index 2af4564ffa..b83cf04954 100644 --- a/frappe/public/js/frappe/widgets/links_widget.js +++ b/frappe/public/js/frappe/widgets/links_widget.js @@ -60,9 +60,7 @@ export default class LinksWidget extends Widget { const get_link_for_item = (item) => { if (is_link_disabled(item)) { - return `${ - item.label ? item.label : item.name - } + return `${item.link_title}