@@ -9,7 +9,7 @@ from functools import lru_cache | |||||
@lru_cache(maxsize=None) | @lru_cache(maxsize=None) | ||||
def fetch_pr_data(pr_number, repo, endpoint): | |||||
def fetch_pr_data(pr_number, repo, endpoint=""): | |||||
api_url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}" | api_url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}" | ||||
if endpoint: | if endpoint: | ||||
@@ -37,7 +37,7 @@ def has_run_ui_tests_label(pr_number, repo="frappe/frappe"): | |||||
return has_label(pr_number, "Run UI Tests", repo) | return has_label(pr_number, "Run UI Tests", repo) | ||||
def has_label(pr_number, label, repo="frappe/frappe"): | def has_label(pr_number, label, repo="frappe/frappe"): | ||||
return any([label["name"] for label in fetch_pr_data(pr_number, repo, "")["labels"] if label["name"] == label]) | |||||
return any([fetched_label["name"] for fetched_label in fetch_pr_data(pr_number, repo)["labels"] if fetched_label["name"] == label]) | |||||
def is_py(file): | def is_py(file): | ||||
return file.endswith("py") | return file.endswith("py") | ||||
@@ -49,7 +49,7 @@ def is_frontend_code(file): | |||||
return file.lower().endswith((".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue")) | return file.lower().endswith((".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue")) | ||||
def is_docs(file): | def is_docs(file): | ||||
regex = re.compile(r'\.(md|png|jpg|jpeg|csv)$|^.github|LICENSE') | |||||
regex = re.compile(r'\.(md|png|jpg|jpeg|csv|svg)$|^.github|LICENSE') | |||||
return bool(regex.search(file)) | return bool(regex.search(file)) | ||||
@@ -31,9 +31,9 @@ jobs: | |||||
uses: actions/checkout@v3 | uses: actions/checkout@v3 | ||||
- name: Setup Python | - name: Setup Python | ||||
uses: "gabrielfalcao/pyenv-action@v9" | |||||
uses: "gabrielfalcao/pyenv-action@v10" | |||||
with: | with: | ||||
versions: 3.10:latest, 3.7:latest, 2.7:latest | |||||
versions: 3.10:latest, 3.7:latest | |||||
- name: Setup Node | - name: Setup Node | ||||
uses: actions/setup-node@v3 | uses: actions/setup-node@v3 | ||||
@@ -120,14 +120,10 @@ jobs: | |||||
cd apps/frappe/ | cd apps/frappe/ | ||||
git remote set-url upstream https://github.com/frappe/frappe.git | git remote set-url upstream https://github.com/frappe/frappe.git | ||||
pyenv global $(pyenv versions | grep '3.7') | |||||
for version in $(seq 12 13) | for version in $(seq 12 13) | ||||
do | do | ||||
echo "Updating to v$version" | echo "Updating to v$version" | ||||
if [ $version == 12 ]; then | |||||
pyenv global $(pyenv versions | grep '2.7') | |||||
elif [ $version == 13 ]; then | |||||
pyenv global $(pyenv versions | grep '3.7') | |||||
fi | |||||
branch_name="version-$version-hotfix" | branch_name="version-$version-hotfix" | ||||
git fetch --depth 1 upstream $branch_name:$branch_name | git fetch --depth 1 upstream $branch_name:$branch_name | ||||
git checkout -q -f $branch_name | git checkout -q -f $branch_name | ||||
@@ -222,10 +222,6 @@ def handle_exception(e): | |||||
or (frappe.local.request.path.startswith("/api/") and not accept_header.startswith("text")) | or (frappe.local.request.path.startswith("/api/") and not accept_header.startswith("text")) | ||||
) | ) | ||||
if frappe.conf.get("developer_mode"): | |||||
# don't fail silently | |||||
print(frappe.get_traceback()) | |||||
if respond_as_json: | if respond_as_json: | ||||
# handle ajax responses first | # handle ajax responses first | ||||
# if the request is ajax, send back the trace or error message | # if the request is ajax, send back the trace or error message | ||||
@@ -289,6 +285,10 @@ def handle_exception(e): | |||||
if return_as_message: | if return_as_message: | ||||
response = get_response("message", http_status_code=http_status_code) | response = get_response("message", http_status_code=http_status_code) | ||||
if frappe.conf.get("developer_mode") and not respond_as_json: | |||||
# don't fail silently for non-json response errors | |||||
print(frappe.get_traceback()) | |||||
return response | return response | ||||
@@ -652,14 +652,14 @@ class DocType(Document): | |||||
remaining_field_names = [f.fieldname for f in self.fields] | remaining_field_names = [f.fieldname for f in self.fields] | ||||
for fieldname in old_field_names: | for fieldname in old_field_names: | ||||
field_dict = list(filter(lambda d: d["fieldname"] == fieldname, docdict["fields"])) | |||||
field_dict = [f for f in docdict["fields"] if f["fieldname"] == fieldname] | |||||
if field_dict: | if field_dict: | ||||
new_field_dicts.append(field_dict[0]) | new_field_dicts.append(field_dict[0]) | ||||
if fieldname in remaining_field_names: | if fieldname in remaining_field_names: | ||||
remaining_field_names.remove(fieldname) | remaining_field_names.remove(fieldname) | ||||
for fieldname in remaining_field_names: | for fieldname in remaining_field_names: | ||||
field_dict = list(filter(lambda d: d["fieldname"] == fieldname, docdict["fields"])) | |||||
field_dict = [f for f in docdict["fields"] if f["fieldname"] == fieldname] | |||||
new_field_dicts.append(field_dict[0]) | new_field_dicts.append(field_dict[0]) | ||||
docdict["fields"] = new_field_dicts | docdict["fields"] = new_field_dicts | ||||
@@ -674,14 +674,14 @@ class DocType(Document): | |||||
remaining_field_names = [f["fieldname"] for f in docdict.get("fields", [])] | remaining_field_names = [f["fieldname"] for f in docdict.get("fields", [])] | ||||
for fieldname in docdict.get("field_order"): | for fieldname in docdict.get("field_order"): | ||||
field_dict = list(filter(lambda d: d["fieldname"] == fieldname, docdict.get("fields", []))) | |||||
field_dict = [f for f in docdict.get("fields", []) if f["fieldname"] == fieldname] | |||||
if field_dict: | if field_dict: | ||||
new_field_dicts.append(field_dict[0]) | new_field_dicts.append(field_dict[0]) | ||||
if fieldname in remaining_field_names: | if fieldname in remaining_field_names: | ||||
remaining_field_names.remove(fieldname) | remaining_field_names.remove(fieldname) | ||||
for fieldname in remaining_field_names: | for fieldname in remaining_field_names: | ||||
field_dict = list(filter(lambda d: d["fieldname"] == fieldname, docdict.get("fields", []))) | |||||
field_dict = [f for f in docdict.get("fields", []) if f["fieldname"] == fieldname] | |||||
new_field_dicts.append(field_dict[0]) | new_field_dicts.append(field_dict[0]) | ||||
docdict["fields"] = new_field_dicts | docdict["fields"] = new_field_dicts | ||||
@@ -1,6 +1,4 @@ | |||||
{ | { | ||||
"_comments": "[]", | |||||
"_liked_by": "[]", | |||||
"actions": [], | "actions": [], | ||||
"allow_import": 1, | "allow_import": 1, | ||||
"autoname": "hash", | "autoname": "hash", | ||||
@@ -26,6 +24,7 @@ | |||||
"fieldtype": "Link", | "fieldtype": "Link", | ||||
"label": "Language", | "label": "Language", | ||||
"options": "Language", | "options": "Language", | ||||
"reqd": 1, | |||||
"search_index": 1 | "search_index": 1 | ||||
}, | }, | ||||
{ | { | ||||
@@ -72,20 +71,23 @@ | |||||
"description": "If your data is in HTML, please copy paste the exact HTML code with the tags.", | "description": "If your data is in HTML, please copy paste the exact HTML code with the tags.", | ||||
"fieldname": "source_text", | "fieldname": "source_text", | ||||
"fieldtype": "Code", | "fieldtype": "Code", | ||||
"label": "Source Text" | |||||
"label": "Source Text", | |||||
"reqd": 1 | |||||
}, | }, | ||||
{ | { | ||||
"fieldname": "translated_text", | "fieldname": "translated_text", | ||||
"fieldtype": "Code", | "fieldtype": "Code", | ||||
"in_list_view": 1, | "in_list_view": 1, | ||||
"label": "Translated Text" | |||||
"label": "Translated Text", | |||||
"reqd": 1 | |||||
} | } | ||||
], | ], | ||||
"links": [], | "links": [], | ||||
"modified": "2021-12-31 10:19:52.541055", | |||||
"modified": "2022-07-04 06:53:54.997004", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "Translation", | "name": "Translation", | ||||
"naming_rule": "Random", | |||||
"owner": "Administrator", | "owner": "Administrator", | ||||
"permissions": [ | "permissions": [ | ||||
{ | { | ||||
@@ -103,6 +105,7 @@ | |||||
], | ], | ||||
"sort_field": "modified", | "sort_field": "modified", | ||||
"sort_order": "DESC", | "sort_order": "DESC", | ||||
"states": [], | |||||
"title_field": "source_text", | "title_field": "source_text", | ||||
"track_changes": 1 | "track_changes": 1 | ||||
} | } |
@@ -1157,10 +1157,7 @@ class Database: | |||||
return INDEX_PATTERN.sub(r"", index_name) | return INDEX_PATTERN.sub(r"", index_name) | ||||
def get_system_setting(self, key): | def get_system_setting(self, key): | ||||
def _load_system_settings(): | |||||
return self.get_singles_dict("System Settings") | |||||
return frappe.cache().get_value("system_settings", _load_system_settings).get(key) | |||||
return frappe.get_system_settings(key) | |||||
def close(self): | def close(self): | ||||
"""Close database connection.""" | """Close database connection.""" | ||||
@@ -33,25 +33,20 @@ def getdoc(doctype, name, user=None): | |||||
if not frappe.db.exists(doctype, name): | if not frappe.db.exists(doctype, name): | ||||
return [] | return [] | ||||
try: | |||||
doc = frappe.get_doc(doctype, name) | |||||
run_onload(doc) | |||||
if not doc.has_permission("read"): | |||||
frappe.flags.error_message = _("Insufficient Permission for {0}").format( | |||||
frappe.bold(doctype + " " + name) | |||||
) | |||||
raise frappe.PermissionError(("read", doctype, name)) | |||||
doc = frappe.get_doc(doctype, name) | |||||
run_onload(doc) | |||||
doc.apply_fieldlevel_read_permissions() | |||||
if not doc.has_permission("read"): | |||||
frappe.flags.error_message = _("Insufficient Permission for {0}").format( | |||||
frappe.bold(doctype + " " + name) | |||||
) | |||||
raise frappe.PermissionError(("read", doctype, name)) | |||||
# add file list | |||||
doc.add_viewed() | |||||
get_docinfo(doc) | |||||
doc.apply_fieldlevel_read_permissions() | |||||
except Exception: | |||||
frappe.errprint(frappe.utils.get_traceback()) | |||||
raise | |||||
# add file list | |||||
doc.add_viewed() | |||||
get_docinfo(doc) | |||||
doc.add_seen() | doc.add_seen() | ||||
set_link_titles(doc) | set_link_titles(doc) | ||||
@@ -10,42 +10,33 @@ from frappe.desk.form.load import run_onload | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def savedocs(doc, action): | def savedocs(doc, action): | ||||
"""save / submit / update doclist""" | """save / submit / update doclist""" | ||||
try: | |||||
doc = frappe.get_doc(json.loads(doc)) | |||||
set_local_name(doc) | |||||
doc = frappe.get_doc(json.loads(doc)) | |||||
set_local_name(doc) | |||||
# action | |||||
doc.docstatus = {"Save": 0, "Submit": 1, "Update": 1, "Cancel": 2}[action] | |||||
# action | |||||
doc.docstatus = {"Save": 0, "Submit": 1, "Update": 1, "Cancel": 2}[action] | |||||
if doc.docstatus == 1: | |||||
doc.submit() | |||||
else: | |||||
doc.save() | |||||
if doc.docstatus == 1: | |||||
doc.submit() | |||||
else: | |||||
doc.save() | |||||
# update recent documents | |||||
run_onload(doc) | |||||
send_updated_docs(doc) | |||||
# update recent documents | |||||
run_onload(doc) | |||||
send_updated_docs(doc) | |||||
frappe.msgprint(frappe._("Saved"), indicator="green", alert=True) | |||||
except Exception: | |||||
frappe.errprint(frappe.utils.get_traceback()) | |||||
raise | |||||
frappe.msgprint(frappe._("Saved"), indicator="green", alert=True) | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def cancel(doctype=None, name=None, workflow_state_fieldname=None, workflow_state=None): | def cancel(doctype=None, name=None, workflow_state_fieldname=None, workflow_state=None): | ||||
"""cancel a doclist""" | """cancel a doclist""" | ||||
try: | |||||
doc = frappe.get_doc(doctype, name) | |||||
if workflow_state_fieldname and workflow_state: | |||||
doc.set(workflow_state_fieldname, workflow_state) | |||||
doc.cancel() | |||||
send_updated_docs(doc) | |||||
frappe.msgprint(frappe._("Cancelled"), indicator="red", alert=True) | |||||
except Exception: | |||||
frappe.errprint(frappe.utils.get_traceback()) | |||||
raise | |||||
doc = frappe.get_doc(doctype, name) | |||||
if workflow_state_fieldname and workflow_state: | |||||
doc.set(workflow_state_fieldname, workflow_state) | |||||
doc.cancel() | |||||
send_updated_docs(doc) | |||||
frappe.msgprint(frappe._("Cancelled"), indicator="red", alert=True) | |||||
def send_updated_docs(doc): | def send_updated_docs(doc): | ||||
@@ -87,7 +87,10 @@ class NamingSeries: | |||||
for count in range(1, 4): | for count in range(1, 4): | ||||
def fake_counter(_prefix, digits): | def fake_counter(_prefix, digits): | ||||
return str(count).zfill(digits) | |||||
# ignore B023: binding `count` is not necessary because | |||||
# function is evaluated immediately and it can not be done | |||||
# because of function signature requirement | |||||
return str(count).zfill(digits) # noqa: B023 | |||||
generated_names.append(parse_naming_series(self.series, doc=doc, number_generator=fake_counter)) | generated_names.append(parse_naming_series(self.series, doc=doc, number_generator=fake_counter)) | ||||
return generated_names | return generated_names | ||||
@@ -226,13 +226,16 @@ frappe.ui.form.Control = class BaseControl { | |||||
} | } | ||||
} | } | ||||
set_model_value(value) { | set_model_value(value) { | ||||
if(this.frm) { | |||||
if (this.frm) { | |||||
this.last_value = value; | this.last_value = value; | ||||
return frappe.model.set_value(this.doctype, this.docname, this.df.fieldname, | return frappe.model.set_value(this.doctype, this.docname, this.df.fieldname, | ||||
value, this.df.fieldtype); | value, this.df.fieldtype); | ||||
} else { | } else { | ||||
if(this.doc) { | |||||
if (this.doc) { | |||||
this.doc[this.df.fieldname] = value; | this.doc[this.df.fieldname] = value; | ||||
} else { | |||||
// case where input is rendered on dialog where doc is not maintained | |||||
this.value = value; | |||||
} | } | ||||
this.set_input(value); | this.set_input(value); | ||||
return Promise.resolve(); | return Promise.resolve(); | ||||
@@ -95,6 +95,7 @@ class TestFmtMoney(unittest.TestCase): | |||||
def test_custom_fmt_money_format(self): | def test_custom_fmt_money_format(self): | ||||
self.assertEqual(fmt_money(100000, format="#,###.##"), "100,000.00") | self.assertEqual(fmt_money(100000, format="#,###.##"), "100,000.00") | ||||
self.assertEqual(fmt_money(None, format="#,###.##"), "0.00") | |||||
if __name__ == "__main__": | if __name__ == "__main__": | ||||
@@ -1111,7 +1111,7 @@ def parse_val(v): | |||||
def fmt_money( | def fmt_money( | ||||
amount: str | float | int, | |||||
amount: str | float | int | None, | |||||
precision: int | None = None, | precision: int | None = None, | ||||
currency: str | None = None, | currency: str | None = None, | ||||
format: str | None = None, | format: str | None = None, | ||||
@@ -1135,6 +1135,9 @@ def fmt_money( | |||||
if isinstance(amount, str): | if isinstance(amount, str): | ||||
amount = flt(amount, precision) | amount = flt(amount, precision) | ||||
if amount is None: | |||||
amount = 0 | |||||
if decimal_str: | if decimal_str: | ||||
decimals_after = str(round(amount % 1, precision)) | decimals_after = str(round(amount % 1, precision)) | ||||
parts = decimals_after.split(".") | parts = decimals_after.split(".") | ||||
@@ -85,7 +85,8 @@ def get_snapshot(exception, context=10): | |||||
def reader(lnum=[lnum]): # noqa | def reader(lnum=[lnum]): # noqa | ||||
try: | try: | ||||
return linecache.getline(file, lnum[0]) | |||||
# B023: function is evaluated immediately, binding not necessary | |||||
return linecache.getline(file, lnum[0]) # noqa: B023 | |||||
finally: | finally: | ||||
lnum[0] += 1 | lnum[0] += 1 | ||||