We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
frappe.db.exists is not available to Server Scripts
**Is your feature request related to a problem? Please describe.**
`frappe.db.exists` is not exposed to **Server Scripts**.

**Describe alternatives you've considered**
Currently I am using `frappe.get_all` with `filters` and `limit=1` to check for existence.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `frappe/utils/safe_exec.py`
Content:
```
1 
2 import os, json, inspect
3 import mimetypes
4 from html2text import html2text
5 from RestrictedPython import compile_restricted, safe_globals
6 import RestrictedPython.Guards
7 import frappe
8 from frappe import _
9 import frappe.utils
10 import frappe.utils.data
11 from frappe.website.utils import (get_shade, get_toc, get_next_link)
12 from frappe.modules import scrub
13 from frappe.www.printview import get_visible_columns
14 import frappe.exceptions
15 import frappe.integrations.utils
16 from frappe.frappeclient import FrappeClient
17 
18 class ServerScriptNotEnabled(frappe.PermissionError):
19 	pass
20 
21 class NamespaceDict(frappe._dict):
22 	"""Raise AttributeError if function not found in namespace"""
23 	def __getattr__(self, key):
24 		ret = self.get(key)
25 		if (not ret and key.startswith("__")) or (key not in self):
26 			def default_function(*args, **kwargs):
27 				raise AttributeError(f"module has no attribute '{key}'")
28 			return default_function
29 		return ret
30 
31 
32 def safe_exec(script, _globals=None, _locals=None):
33 	# server scripts can be disabled via site_config.json
34 	# they are enabled by default
35 	if 'server_script_enabled' in frappe.conf:
36 		enabled = frappe.conf.server_script_enabled
37 	else:
38 		enabled = True
39 
40 	if not enabled:
41 		frappe.throw(_('Please Enable Server Scripts'), ServerScriptNotEnabled)
42 
43 	# build globals
44 	exec_globals = get_safe_globals()
45 	if _globals:
46 		exec_globals.update(_globals)
47 
48 	# execute script compiled by RestrictedPython
49 	exec(compile_restricted(script), exec_globals, _locals) # pylint: disable=exec-used
50 
51 	return exec_globals, _locals
52 
53 def get_safe_globals():
54 	datautils = frappe._dict()
55 	if frappe.db:
56 		date_format = frappe.db.get_default("date_format") or "yyyy-mm-dd"
57 		time_format = frappe.db.get_default("time_format") or "HH:mm:ss"
58 	else:
59 		date_format = "yyyy-mm-dd"
60 		time_format = "HH:mm:ss"
61 
62 	add_data_utils(datautils)
63 
64 	if "_" in getattr(frappe.local, 'form_dict', {}):
65 		del frappe.local.form_dict["_"]
66 
67 	user = getattr(frappe.local, "session", None) and frappe.local.session.user or "Guest"
68 
69 	out = NamespaceDict(
70 		# make available limited methods of frappe
71 		json=NamespaceDict(
72 			loads = json.loads,
73 			dumps = json.dumps),
74 		dict=dict,
75 		log=frappe.log,
76 		_dict=frappe._dict,
77 		frappe=NamespaceDict(
78 			flags=frappe._dict(),
79 			format=frappe.format_value,
80 			format_value=frappe.format_value,
81 			date_format=date_format,
82 			time_format=time_format,
83 			format_date=frappe.utils.data.global_date_format,
84 			form_dict=getattr(frappe.local, 'form_dict', {}),
85 			bold=frappe.bold,
86 			copy_doc=frappe.copy_doc,
87 			errprint=frappe.errprint,
88 
89 			get_meta=frappe.get_meta,
90 			get_doc=frappe.get_doc,
91 			get_cached_doc=frappe.get_cached_doc,
92 			get_list=frappe.get_list,
93 			get_all=frappe.get_all,
94 			get_system_settings=frappe.get_system_settings,
95 			rename_doc=frappe.rename_doc,
96 
97 			utils=datautils,
98 			get_url=frappe.utils.get_url,
99 			render_template=frappe.render_template,
100 			msgprint=frappe.msgprint,
101 			throw=frappe.throw,
102 			sendmail = frappe.sendmail,
103 			get_print = frappe.get_print,
104 			attach_print = frappe.attach_print,
105 
106 			user=user,
107 			get_fullname=frappe.utils.get_fullname,
108 			get_gravatar=frappe.utils.get_gravatar_url,
109 			full_name=frappe.local.session.data.full_name if getattr(frappe.local, "session", None) else "Guest",
110 			request=getattr(frappe.local, 'request', {}),
111 			session=frappe._dict(
112 				user=user,
113 				csrf_token=frappe.local.session.data.csrf_token if getattr(frappe.local, "session", None) else ''
114 			),
115 			make_get_request = frappe.integrations.utils.make_get_request,
116 			make_post_request = frappe.integrations.utils.make_post_request,
117 			socketio_port=frappe.conf.socketio_port,
118 			get_hooks=frappe.get_hooks,
119 			sanitize_html=frappe.utils.sanitize_html,
120 			log_error=frappe.log_error
121 		),
122 		FrappeClient=FrappeClient,
123 		style=frappe._dict(
124 			border_color='#d1d8dd'
125 		),
126 		get_toc=get_toc,
127 		get_next_link=get_next_link,
128 		_=frappe._,
129 		get_shade=get_shade,
130 		scrub=scrub,
131 		guess_mimetype=mimetypes.guess_type,
132 		html2text=html2text,
133 		dev_server=1 if frappe._dev_server else 0,
134 		run_script=run_script
135 	)
136 
137 	add_module_properties(frappe.exceptions, out.frappe, lambda obj: inspect.isclass(obj) and issubclass(obj, Exception))
138 
139 	if not frappe.flags.in_setup_help:
140 		out.get_visible_columns = get_visible_columns
141 		out.frappe.date_format = date_format
142 		out.frappe.time_format = time_format
143 		out.frappe.db = NamespaceDict(
144 			get_list = frappe.get_list,
145 			get_all = frappe.get_all,
146 			get_value = frappe.db.get_value,
147 			set_value = frappe.db.set_value,
148 			get_single_value = frappe.db.get_single_value,
149 			get_default = frappe.db.get_default,
150 			count = frappe.db.count,
151 			min = frappe.db.min,
152 			max = frappe.db.max,
153 			avg = frappe.db.avg,
154 			sum = frappe.db.sum,
155 			escape = frappe.db.escape,
156 			sql = read_sql
157 		)
158 
159 	if frappe.response:
160 		out.frappe.response = frappe.response
161 
162 	out.update(safe_globals)
163 
164 	# default writer allows write access
165 	out._write_ = _write
166 	out._getitem_ = _getitem
167 	out._getattr_ = _getattr
168 
169 	# allow iterators and list comprehension
170 	out._getiter_ = iter
171 	out._iter_unpack_sequence_ = RestrictedPython.Guards.guarded_iter_unpack_sequence
172 	out.sorted = sorted
173 
174 	return out
175 
176 def read_sql(query, *args, **kwargs):
177 	'''a wrapper for frappe.db.sql to allow reads'''
178 	if query.strip().split(None, 1)[0].lower() == 'select':
179 		return frappe.db.sql(query, *args, **kwargs)
180 	else:
181 		raise frappe.PermissionError('Only SELECT SQL allowed in scripting')
182 
183 def run_script(script):
184 	'''run another server script'''
185 	return frappe.get_doc('Server Script', script).execute_method()
186 
187 def _getitem(obj, key):
188 	# guard function for RestrictedPython
189 	# allow any key to be accessed as long as it does not start with underscore
190 	if isinstance(key, str) and key.startswith('_'):
191 		raise SyntaxError('Key starts with _')
192 	return obj[key]
193 
194 def _getattr(object, name, default=None):
195 	# guard function for RestrictedPython
196 	# allow any key to be accessed as long as
197 	# 1. it does not start with an underscore (safer_getattr)
198 	# 2. it is not an UNSAFE_ATTRIBUTES
199 
200 	UNSAFE_ATTRIBUTES = {
201 		# Generator Attributes
202 		"gi_frame", "gi_code",
203 		# Coroutine Attributes
204 		"cr_frame", "cr_code", "cr_origin",
205 		# Async Generator Attributes
206 		"ag_code", "ag_frame",
207 		# Traceback Attributes
208 		"tb_frame", "tb_next",
209 	}
210 
211 	if isinstance(name, str) and (name in UNSAFE_ATTRIBUTES):
212 		raise SyntaxError("{name} is an unsafe attribute".format(name=name))
213 	return RestrictedPython.Guards.safer_getattr(object, name, default=default)
214 
215 def _write(obj):
216 	# guard function for RestrictedPython
217 	# allow writing to any object
218 	return obj
219 
220 def add_data_utils(data):
221 	for key, obj in frappe.utils.data.__dict__.items():
222 		if key in VALID_UTILS:
223 			data[key] = obj
224 
225 def add_module_properties(module, data, filter_method):
226 	for key, obj in module.__dict__.items():
227 		if key.startswith("_"):
228 			# ignore
229 			continue
230 
231 		if filter_method(obj):
232 			# only allow functions
233 			data[key] = obj
234 
235 VALID_UTILS = (
236 "DATE_FORMAT",
237 "TIME_FORMAT",
238 "DATETIME_FORMAT",
239 "is_invalid_date_string",
240 "getdate",
241 "get_datetime",
242 "to_timedelta",
243 "get_timedelta",
244 "add_to_date",
245 "add_days",
246 "add_months",
247 "add_years",
248 "date_diff",
249 "month_diff",
250 "time_diff",
251 "time_diff_in_seconds",
252 "time_diff_in_hours",
253 "now_datetime",
254 "get_timestamp",
255 "get_eta",
256 "get_time_zone",
257 "convert_utc_to_user_timezone",
258 "now",
259 "nowdate",
260 "today",
261 "nowtime",
262 "get_first_day",
263 "get_quarter_start",
264 "get_first_day_of_week",
265 "get_year_start",
266 "get_last_day_of_week",
267 "get_last_day",
268 "get_time",
269 "get_datetime_in_timezone",
270 "get_datetime_str",
271 "get_date_str",
272 "get_time_str",
273 "get_user_date_format",
274 "get_user_time_format",
275 "format_date",
276 "format_time",
277 "format_datetime",
278 "format_duration",
279 "get_weekdays",
280 "get_weekday",
281 "get_timespan_date_range",
282 "global_date_format",
283 "has_common",
284 "flt",
285 "cint",
286 "floor",
287 "ceil",
288 "cstr",
289 "rounded",
290 "remainder",
291 "safe_div",
292 "round_based_on_smallest_currency_fraction",
293 "encode",
294 "parse_val",
295 "fmt_money",
296 "get_number_format_info",
297 "money_in_words",
298 "in_words",
299 "is_html",
300 "is_image",
301 "get_thumbnail_base64_for_image",
302 "image_to_base64",
303 "strip_html",
304 "escape_html",
305 "pretty_date",
306 "comma_or",
307 "comma_and",
308 "comma_sep",
309 "new_line_sep",
310 "filter_strip_join",
311 "get_url",
312 "get_host_name_from_request",
313 "url_contains_port",
314 "get_host_name",
315 "get_link_to_form",
316 "get_link_to_report",
317 "get_absolute_url",
318 "get_url_to_form",
319 "get_url_to_list",
320 "get_url_to_report",
321 "get_url_to_report_with_filters",
322 "evaluate_filters",
323 "compare",
324 "get_filter",
325 "make_filter_tuple",
326 "make_filter_dict",
327 "sanitize_column",
328 "scrub_urls",
329 "expand_relative_urls",
330 "quoted",
331 "quote_urls",
332 "unique",
333 "strip",
334 "to_markdown",
335 "md_to_html",
336 "markdown",
337 "is_subset",
338 "generate_hash",
339 "formatdate",
340 "get_user_info_for_avatar",
341 "get_abbr"
342 )
343
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py
--- a/frappe/utils/safe_exec.py
+++ b/frappe/utils/safe_exec.py
@@ -147,6 +147,7 @@
 			set_value = frappe.db.set_value,
 			get_single_value = frappe.db.get_single_value,
 			get_default = frappe.db.get_default,
+			exists = frappe.db.exists,
 			count = frappe.db.count,
 			min = frappe.db.min,
 			max = frappe.db.max,

{"golden_diff": "diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py\n--- a/frappe/utils/safe_exec.py\n+++ b/frappe/utils/safe_exec.py\n@@ -147,6 +147,7 @@\n \t\t\tset_value = frappe.db.set_value,\n \t\t\tget_single_value = frappe.db.get_single_value,\n \t\t\tget_default = frappe.db.get_default,\n+\t\t\texists = frappe.db.exists,\n \t\t\tcount = frappe.db.count,\n \t\t\tmin = frappe.db.min,\n \t\t\tmax = frappe.db.max,\n", "issue": "frappe.db.exists is not available to Server Scripts\n**Is your feature request related to a problem? Please describe.**\r\n`frappe.db.exists` is not exposed to **Server Scripts**.\r\n\r\n**Describe alternatives you've considered**\r\nCurrently I am using `frappe.get_all` with `filters` and `limit=1` to check for existence.\r\n\n", "before_files": [{"content": "\nimport os, json, inspect\nimport mimetypes\nfrom html2text import html2text\nfrom RestrictedPython import compile_restricted, safe_globals\nimport RestrictedPython.Guards\nimport frappe\nfrom frappe import _\nimport frappe.utils\nimport frappe.utils.data\nfrom frappe.website.utils import (get_shade, get_toc, get_next_link)\nfrom frappe.modules import scrub\nfrom frappe.www.printview import get_visible_columns\nimport frappe.exceptions\nimport frappe.integrations.utils\nfrom frappe.frappeclient import FrappeClient\n\nclass ServerScriptNotEnabled(frappe.PermissionError):\n\tpass\n\nclass NamespaceDict(frappe._dict):\n\t\"\"\"Raise AttributeError if function not found in namespace\"\"\"\n\tdef __getattr__(self, key):\n\t\tret = self.get(key)\n\t\tif (not ret and key.startswith(\"__\")) or (key not in self):\n\t\t\tdef default_function(*args, **kwargs):\n\t\t\t\traise AttributeError(f\"module has no attribute '{key}'\")\n\t\t\treturn default_function\n\t\treturn ret\n\n\ndef safe_exec(script, _globals=None, _locals=None):\n\t# server scripts can be disabled via site_config.json\n\t# they are enabled by default\n\tif 'server_script_enabled' in frappe.conf:\n\t\tenabled = frappe.conf.server_script_enabled\n\telse:\n\t\tenabled = True\n\n\tif not enabled:\n\t\tfrappe.throw(_('Please Enable Server Scripts'), ServerScriptNotEnabled)\n\n\t# build globals\n\texec_globals = get_safe_globals()\n\tif _globals:\n\t\texec_globals.update(_globals)\n\n\t# execute script compiled by RestrictedPython\n\texec(compile_restricted(script), exec_globals, _locals) # pylint: disable=exec-used\n\n\treturn exec_globals, _locals\n\ndef get_safe_globals():\n\tdatautils = frappe._dict()\n\tif frappe.db:\n\t\tdate_format = frappe.db.get_default(\"date_format\") or \"yyyy-mm-dd\"\n\t\ttime_format = frappe.db.get_default(\"time_format\") or \"HH:mm:ss\"\n\telse:\n\t\tdate_format = \"yyyy-mm-dd\"\n\t\ttime_format = \"HH:mm:ss\"\n\n\tadd_data_utils(datautils)\n\n\tif \"_\" in getattr(frappe.local, 'form_dict', {}):\n\t\tdel frappe.local.form_dict[\"_\"]\n\n\tuser = getattr(frappe.local, \"session\", None) and frappe.local.session.user or \"Guest\"\n\n\tout = NamespaceDict(\n\t\t# make available limited methods of frappe\n\t\tjson=NamespaceDict(\n\t\t\tloads = json.loads,\n\t\t\tdumps = json.dumps),\n\t\tdict=dict,\n\t\tlog=frappe.log,\n\t\t_dict=frappe._dict,\n\t\tfrappe=NamespaceDict(\n\t\t\tflags=frappe._dict(),\n\t\t\tformat=frappe.format_value,\n\t\t\tformat_value=frappe.format_value,\n\t\t\tdate_format=date_format,\n\t\t\ttime_format=time_format,\n\t\t\tformat_date=frappe.utils.data.global_date_format,\n\t\t\tform_dict=getattr(frappe.local, 'form_dict', {}),\n\t\t\tbold=frappe.bold,\n\t\t\tcopy_doc=frappe.copy_doc,\n\t\t\terrprint=frappe.errprint,\n\n\t\t\tget_meta=frappe.get_meta,\n\t\t\tget_doc=frappe.get_doc,\n\t\t\tget_cached_doc=frappe.get_cached_doc,\n\t\t\tget_list=frappe.get_list,\n\t\t\tget_all=frappe.get_all,\n\t\t\tget_system_settings=frappe.get_system_settings,\n\t\t\trename_doc=frappe.rename_doc,\n\n\t\t\tutils=datautils,\n\t\t\tget_url=frappe.utils.get_url,\n\t\t\trender_template=frappe.render_template,\n\t\t\tmsgprint=frappe.msgprint,\n\t\t\tthrow=frappe.throw,\n\t\t\tsendmail = frappe.sendmail,\n\t\t\tget_print = frappe.get_print,\n\t\t\tattach_print = frappe.attach_print,\n\n\t\t\tuser=user,\n\t\t\tget_fullname=frappe.utils.get_fullname,\n\t\t\tget_gravatar=frappe.utils.get_gravatar_url,\n\t\t\tfull_name=frappe.local.session.data.full_name if getattr(frappe.local, \"session\", None) else \"Guest\",\n\t\t\trequest=getattr(frappe.local, 'request', {}),\n\t\t\tsession=frappe._dict(\n\t\t\t\tuser=user,\n\t\t\t\tcsrf_token=frappe.local.session.data.csrf_token if getattr(frappe.local, \"session\", None) else ''\n\t\t\t),\n\t\t\tmake_get_request = frappe.integrations.utils.make_get_request,\n\t\t\tmake_post_request = frappe.integrations.utils.make_post_request,\n\t\t\tsocketio_port=frappe.conf.socketio_port,\n\t\t\tget_hooks=frappe.get_hooks,\n\t\t\tsanitize_html=frappe.utils.sanitize_html,\n\t\t\tlog_error=frappe.log_error\n\t\t),\n\t\tFrappeClient=FrappeClient,\n\t\tstyle=frappe._dict(\n\t\t\tborder_color='#d1d8dd'\n\t\t),\n\t\tget_toc=get_toc,\n\t\tget_next_link=get_next_link,\n\t\t_=frappe._,\n\t\tget_shade=get_shade,\n\t\tscrub=scrub,\n\t\tguess_mimetype=mimetypes.guess_type,\n\t\thtml2text=html2text,\n\t\tdev_server=1 if frappe._dev_server else 0,\n\t\trun_script=run_script\n\t)\n\n\tadd_module_properties(frappe.exceptions, out.frappe, lambda obj: inspect.isclass(obj) and issubclass(obj, Exception))\n\n\tif not frappe.flags.in_setup_help:\n\t\tout.get_visible_columns = get_visible_columns\n\t\tout.frappe.date_format = date_format\n\t\tout.frappe.time_format = time_format\n\t\tout.frappe.db = NamespaceDict(\n\t\t\tget_list = frappe.get_list,\n\t\t\tget_all = frappe.get_all,\n\t\t\tget_value = frappe.db.get_value,\n\t\t\tset_value = frappe.db.set_value,\n\t\t\tget_single_value = frappe.db.get_single_value,\n\t\t\tget_default = frappe.db.get_default,\n\t\t\tcount = frappe.db.count,\n\t\t\tmin = frappe.db.min,\n\t\t\tmax = frappe.db.max,\n\t\t\tavg = frappe.db.avg,\n\t\t\tsum = frappe.db.sum,\n\t\t\tescape = frappe.db.escape,\n\t\t\tsql = read_sql\n\t\t)\n\n\tif frappe.response:\n\t\tout.frappe.response = frappe.response\n\n\tout.update(safe_globals)\n\n\t# default writer allows write access\n\tout._write_ = _write\n\tout._getitem_ = _getitem\n\tout._getattr_ = _getattr\n\n\t# allow iterators and list comprehension\n\tout._getiter_ = iter\n\tout._iter_unpack_sequence_ = RestrictedPython.Guards.guarded_iter_unpack_sequence\n\tout.sorted = sorted\n\n\treturn out\n\ndef read_sql(query, *args, **kwargs):\n\t'''a wrapper for frappe.db.sql to allow reads'''\n\tif query.strip().split(None, 1)[0].lower() == 'select':\n\t\treturn frappe.db.sql(query, *args, **kwargs)\n\telse:\n\t\traise frappe.PermissionError('Only SELECT SQL allowed in scripting')\n\ndef run_script(script):\n\t'''run another server script'''\n\treturn frappe.get_doc('Server Script', script).execute_method()\n\ndef _getitem(obj, key):\n\t# guard function for RestrictedPython\n\t# allow any key to be accessed as long as it does not start with underscore\n\tif isinstance(key, str) and key.startswith('_'):\n\t\traise SyntaxError('Key starts with _')\n\treturn obj[key]\n\ndef _getattr(object, name, default=None):\n\t# guard function for RestrictedPython\n\t# allow any key to be accessed as long as\n\t# 1. it does not start with an underscore (safer_getattr)\n\t# 2. it is not an UNSAFE_ATTRIBUTES\n\n\tUNSAFE_ATTRIBUTES = {\n\t\t# Generator Attributes\n\t\t\"gi_frame\", \"gi_code\",\n\t\t# Coroutine Attributes\n\t\t\"cr_frame\", \"cr_code\", \"cr_origin\",\n\t\t# Async Generator Attributes\n\t\t\"ag_code\", \"ag_frame\",\n\t\t# Traceback Attributes\n\t\t\"tb_frame\", \"tb_next\",\n\t}\n\n\tif isinstance(name, str) and (name in UNSAFE_ATTRIBUTES):\n\t\traise SyntaxError(\"{name} is an unsafe attribute\".format(name=name))\n\treturn RestrictedPython.Guards.safer_getattr(object, name, default=default)\n\ndef _write(obj):\n\t# guard function for RestrictedPython\n\t# allow writing to any object\n\treturn obj\n\ndef add_data_utils(data):\n\tfor key, obj in frappe.utils.data.__dict__.items():\n\t\tif key in VALID_UTILS:\n\t\t\tdata[key] = obj\n\ndef add_module_properties(module, data, filter_method):\n\tfor key, obj in module.__dict__.items():\n\t\tif key.startswith(\"_\"):\n\t\t\t# ignore\n\t\t\tcontinue\n\n\t\tif filter_method(obj):\n\t\t\t# only allow functions\n\t\t\tdata[key] = obj\n\nVALID_UTILS = (\n\"DATE_FORMAT\",\n\"TIME_FORMAT\",\n\"DATETIME_FORMAT\",\n\"is_invalid_date_string\",\n\"getdate\",\n\"get_datetime\",\n\"to_timedelta\",\n\"get_timedelta\",\n\"add_to_date\",\n\"add_days\",\n\"add_months\",\n\"add_years\",\n\"date_diff\",\n\"month_diff\",\n\"time_diff\",\n\"time_diff_in_seconds\",\n\"time_diff_in_hours\",\n\"now_datetime\",\n\"get_timestamp\",\n\"get_eta\",\n\"get_time_zone\",\n\"convert_utc_to_user_timezone\",\n\"now\",\n\"nowdate\",\n\"today\",\n\"nowtime\",\n\"get_first_day\",\n\"get_quarter_start\",\n\"get_first_day_of_week\",\n\"get_year_start\",\n\"get_last_day_of_week\",\n\"get_last_day\",\n\"get_time\",\n\"get_datetime_in_timezone\",\n\"get_datetime_str\",\n\"get_date_str\",\n\"get_time_str\",\n\"get_user_date_format\",\n\"get_user_time_format\",\n\"format_date\",\n\"format_time\",\n\"format_datetime\",\n\"format_duration\",\n\"get_weekdays\",\n\"get_weekday\",\n\"get_timespan_date_range\",\n\"global_date_format\",\n\"has_common\",\n\"flt\",\n\"cint\",\n\"floor\",\n\"ceil\",\n\"cstr\",\n\"rounded\",\n\"remainder\",\n\"safe_div\",\n\"round_based_on_smallest_currency_fraction\",\n\"encode\",\n\"parse_val\",\n\"fmt_money\",\n\"get_number_format_info\",\n\"money_in_words\",\n\"in_words\",\n\"is_html\",\n\"is_image\",\n\"get_thumbnail_base64_for_image\",\n\"image_to_base64\",\n\"strip_html\",\n\"escape_html\",\n\"pretty_date\",\n\"comma_or\",\n\"comma_and\",\n\"comma_sep\",\n\"new_line_sep\",\n\"filter_strip_join\",\n\"get_url\",\n\"get_host_name_from_request\",\n\"url_contains_port\",\n\"get_host_name\",\n\"get_link_to_form\",\n\"get_link_to_report\",\n\"get_absolute_url\",\n\"get_url_to_form\",\n\"get_url_to_list\",\n\"get_url_to_report\",\n\"get_url_to_report_with_filters\",\n\"evaluate_filters\",\n\"compare\",\n\"get_filter\",\n\"make_filter_tuple\",\n\"make_filter_dict\",\n\"sanitize_column\",\n\"scrub_urls\",\n\"expand_relative_urls\",\n\"quoted\",\n\"quote_urls\",\n\"unique\",\n\"strip\",\n\"to_markdown\",\n\"md_to_html\",\n\"markdown\",\n\"is_subset\",\n\"generate_hash\",\n\"formatdate\",\n\"get_user_info_for_avatar\",\n\"get_abbr\"\n)\n", "path": "frappe/utils/safe_exec.py"}], "after_files": [{"content": "\nimport os, json, inspect\nimport mimetypes\nfrom html2text import html2text\nfrom RestrictedPython import compile_restricted, safe_globals\nimport RestrictedPython.Guards\nimport frappe\nfrom frappe import _\nimport frappe.utils\nimport frappe.utils.data\nfrom frappe.website.utils import (get_shade, get_toc, get_next_link)\nfrom frappe.modules import scrub\nfrom frappe.www.printview import get_visible_columns\nimport frappe.exceptions\nimport frappe.integrations.utils\nfrom frappe.frappeclient import FrappeClient\n\nclass ServerScriptNotEnabled(frappe.PermissionError):\n\tpass\n\nclass NamespaceDict(frappe._dict):\n\t\"\"\"Raise AttributeError if function not found in namespace\"\"\"\n\tdef __getattr__(self, key):\n\t\tret = self.get(key)\n\t\tif (not ret and key.startswith(\"__\")) or (key not in self):\n\t\t\tdef default_function(*args, **kwargs):\n\t\t\t\traise AttributeError(f\"module has no attribute '{key}'\")\n\t\t\treturn default_function\n\t\treturn ret\n\n\ndef safe_exec(script, _globals=None, _locals=None):\n\t# server scripts can be disabled via site_config.json\n\t# they are enabled by default\n\tif 'server_script_enabled' in frappe.conf:\n\t\tenabled = frappe.conf.server_script_enabled\n\telse:\n\t\tenabled = True\n\n\tif not enabled:\n\t\tfrappe.throw(_('Please Enable Server Scripts'), ServerScriptNotEnabled)\n\n\t# build globals\n\texec_globals = get_safe_globals()\n\tif _globals:\n\t\texec_globals.update(_globals)\n\n\t# execute script compiled by RestrictedPython\n\texec(compile_restricted(script), exec_globals, _locals) # pylint: disable=exec-used\n\n\treturn exec_globals, _locals\n\ndef get_safe_globals():\n\tdatautils = frappe._dict()\n\tif frappe.db:\n\t\tdate_format = frappe.db.get_default(\"date_format\") or \"yyyy-mm-dd\"\n\t\ttime_format = frappe.db.get_default(\"time_format\") or \"HH:mm:ss\"\n\telse:\n\t\tdate_format = \"yyyy-mm-dd\"\n\t\ttime_format = \"HH:mm:ss\"\n\n\tadd_data_utils(datautils)\n\n\tif \"_\" in getattr(frappe.local, 'form_dict', {}):\n\t\tdel frappe.local.form_dict[\"_\"]\n\n\tuser = getattr(frappe.local, \"session\", None) and frappe.local.session.user or \"Guest\"\n\n\tout = NamespaceDict(\n\t\t# make available limited methods of frappe\n\t\tjson=NamespaceDict(\n\t\t\tloads = json.loads,\n\t\t\tdumps = json.dumps),\n\t\tdict=dict,\n\t\tlog=frappe.log,\n\t\t_dict=frappe._dict,\n\t\tfrappe=NamespaceDict(\n\t\t\tflags=frappe._dict(),\n\t\t\tformat=frappe.format_value,\n\t\t\tformat_value=frappe.format_value,\n\t\t\tdate_format=date_format,\n\t\t\ttime_format=time_format,\n\t\t\tformat_date=frappe.utils.data.global_date_format,\n\t\t\tform_dict=getattr(frappe.local, 'form_dict', {}),\n\t\t\tbold=frappe.bold,\n\t\t\tcopy_doc=frappe.copy_doc,\n\t\t\terrprint=frappe.errprint,\n\n\t\t\tget_meta=frappe.get_meta,\n\t\t\tget_doc=frappe.get_doc,\n\t\t\tget_cached_doc=frappe.get_cached_doc,\n\t\t\tget_list=frappe.get_list,\n\t\t\tget_all=frappe.get_all,\n\t\t\tget_system_settings=frappe.get_system_settings,\n\t\t\trename_doc=frappe.rename_doc,\n\n\t\t\tutils=datautils,\n\t\t\tget_url=frappe.utils.get_url,\n\t\t\trender_template=frappe.render_template,\n\t\t\tmsgprint=frappe.msgprint,\n\t\t\tthrow=frappe.throw,\n\t\t\tsendmail = frappe.sendmail,\n\t\t\tget_print = frappe.get_print,\n\t\t\tattach_print = frappe.attach_print,\n\n\t\t\tuser=user,\n\t\t\tget_fullname=frappe.utils.get_fullname,\n\t\t\tget_gravatar=frappe.utils.get_gravatar_url,\n\t\t\tfull_name=frappe.local.session.data.full_name if getattr(frappe.local, \"session\", None) else \"Guest\",\n\t\t\trequest=getattr(frappe.local, 'request', {}),\n\t\t\tsession=frappe._dict(\n\t\t\t\tuser=user,\n\t\t\t\tcsrf_token=frappe.local.session.data.csrf_token if getattr(frappe.local, \"session\", None) else ''\n\t\t\t),\n\t\t\tmake_get_request = frappe.integrations.utils.make_get_request,\n\t\t\tmake_post_request = frappe.integrations.utils.make_post_request,\n\t\t\tsocketio_port=frappe.conf.socketio_port,\n\t\t\tget_hooks=frappe.get_hooks,\n\t\t\tsanitize_html=frappe.utils.sanitize_html,\n\t\t\tlog_error=frappe.log_error\n\t\t),\n\t\tFrappeClient=FrappeClient,\n\t\tstyle=frappe._dict(\n\t\t\tborder_color='#d1d8dd'\n\t\t),\n\t\tget_toc=get_toc,\n\t\tget_next_link=get_next_link,\n\t\t_=frappe._,\n\t\tget_shade=get_shade,\n\t\tscrub=scrub,\n\t\tguess_mimetype=mimetypes.guess_type,\n\t\thtml2text=html2text,\n\t\tdev_server=1 if frappe._dev_server else 0,\n\t\trun_script=run_script\n\t)\n\n\tadd_module_properties(frappe.exceptions, out.frappe, lambda obj: inspect.isclass(obj) and issubclass(obj, Exception))\n\n\tif not frappe.flags.in_setup_help:\n\t\tout.get_visible_columns = get_visible_columns\n\t\tout.frappe.date_format = date_format\n\t\tout.frappe.time_format = time_format\n\t\tout.frappe.db = NamespaceDict(\n\t\t\tget_list = frappe.get_list,\n\t\t\tget_all = frappe.get_all,\n\t\t\tget_value = frappe.db.get_value,\n\t\t\tset_value = frappe.db.set_value,\n\t\t\tget_single_value = frappe.db.get_single_value,\n\t\t\tget_default = frappe.db.get_default,\n\t\t\texists = frappe.db.exists,\n\t\t\tcount = frappe.db.count,\n\t\t\tmin = frappe.db.min,\n\t\t\tmax = frappe.db.max,\n\t\t\tavg = frappe.db.avg,\n\t\t\tsum = frappe.db.sum,\n\t\t\tescape = frappe.db.escape,\n\t\t\tsql = read_sql\n\t\t)\n\n\tif frappe.response:\n\t\tout.frappe.response = frappe.response\n\n\tout.update(safe_globals)\n\n\t# default writer allows write access\n\tout._write_ = _write\n\tout._getitem_ = _getitem\n\tout._getattr_ = _getattr\n\n\t# allow iterators and list comprehension\n\tout._getiter_ = iter\n\tout._iter_unpack_sequence_ = RestrictedPython.Guards.guarded_iter_unpack_sequence\n\tout.sorted = sorted\n\n\treturn out\n\ndef read_sql(query, *args, **kwargs):\n\t'''a wrapper for frappe.db.sql to allow reads'''\n\tif query.strip().split(None, 1)[0].lower() == 'select':\n\t\treturn frappe.db.sql(query, *args, **kwargs)\n\telse:\n\t\traise frappe.PermissionError('Only SELECT SQL allowed in scripting')\n\ndef run_script(script):\n\t'''run another server script'''\n\treturn frappe.get_doc('Server Script', script).execute_method()\n\ndef _getitem(obj, key):\n\t# guard function for RestrictedPython\n\t# allow any key to be accessed as long as it does not start with underscore\n\tif isinstance(key, str) and key.startswith('_'):\n\t\traise SyntaxError('Key starts with _')\n\treturn obj[key]\n\ndef _getattr(object, name, default=None):\n\t# guard function for RestrictedPython\n\t# allow any key to be accessed as long as\n\t# 1. it does not start with an underscore (safer_getattr)\n\t# 2. it is not an UNSAFE_ATTRIBUTES\n\n\tUNSAFE_ATTRIBUTES = {\n\t\t# Generator Attributes\n\t\t\"gi_frame\", \"gi_code\",\n\t\t# Coroutine Attributes\n\t\t\"cr_frame\", \"cr_code\", \"cr_origin\",\n\t\t# Async Generator Attributes\n\t\t\"ag_code\", \"ag_frame\",\n\t\t# Traceback Attributes\n\t\t\"tb_frame\", \"tb_next\",\n\t}\n\n\tif isinstance(name, str) and (name in UNSAFE_ATTRIBUTES):\n\t\traise SyntaxError(\"{name} is an unsafe attribute\".format(name=name))\n\treturn RestrictedPython.Guards.safer_getattr(object, name, default=default)\n\ndef _write(obj):\n\t# guard function for RestrictedPython\n\t# allow writing to any object\n\treturn obj\n\ndef add_data_utils(data):\n\tfor key, obj in frappe.utils.data.__dict__.items():\n\t\tif key in VALID_UTILS:\n\t\t\tdata[key] = obj\n\ndef add_module_properties(module, data, filter_method):\n\tfor key, obj in module.__dict__.items():\n\t\tif key.startswith(\"_\"):\n\t\t\t# ignore\n\t\t\tcontinue\n\n\t\tif filter_method(obj):\n\t\t\t# only allow functions\n\t\t\tdata[key] = obj\n\nVALID_UTILS = (\n\"DATE_FORMAT\",\n\"TIME_FORMAT\",\n\"DATETIME_FORMAT\",\n\"is_invalid_date_string\",\n\"getdate\",\n\"get_datetime\",\n\"to_timedelta\",\n\"get_timedelta\",\n\"add_to_date\",\n\"add_days\",\n\"add_months\",\n\"add_years\",\n\"date_diff\",\n\"month_diff\",\n\"time_diff\",\n\"time_diff_in_seconds\",\n\"time_diff_in_hours\",\n\"now_datetime\",\n\"get_timestamp\",\n\"get_eta\",\n\"get_time_zone\",\n\"convert_utc_to_user_timezone\",\n\"now\",\n\"nowdate\",\n\"today\",\n\"nowtime\",\n\"get_first_day\",\n\"get_quarter_start\",\n\"get_first_day_of_week\",\n\"get_year_start\",\n\"get_last_day_of_week\",\n\"get_last_day\",\n\"get_time\",\n\"get_datetime_in_timezone\",\n\"get_datetime_str\",\n\"get_date_str\",\n\"get_time_str\",\n\"get_user_date_format\",\n\"get_user_time_format\",\n\"format_date\",\n\"format_time\",\n\"format_datetime\",\n\"format_duration\",\n\"get_weekdays\",\n\"get_weekday\",\n\"get_timespan_date_range\",\n\"global_date_format\",\n\"has_common\",\n\"flt\",\n\"cint\",\n\"floor\",\n\"ceil\",\n\"cstr\",\n\"rounded\",\n\"remainder\",\n\"safe_div\",\n\"round_based_on_smallest_currency_fraction\",\n\"encode\",\n\"parse_val\",\n\"fmt_money\",\n\"get_number_format_info\",\n\"money_in_words\",\n\"in_words\",\n\"is_html\",\n\"is_image\",\n\"get_thumbnail_base64_for_image\",\n\"image_to_base64\",\n\"strip_html\",\n\"escape_html\",\n\"pretty_date\",\n\"comma_or\",\n\"comma_and\",\n\"comma_sep\",\n\"new_line_sep\",\n\"filter_strip_join\",\n\"get_url\",\n\"get_host_name_from_request\",\n\"url_contains_port\",\n\"get_host_name\",\n\"get_link_to_form\",\n\"get_link_to_report\",\n\"get_absolute_url\",\n\"get_url_to_form\",\n\"get_url_to_list\",\n\"get_url_to_report\",\n\"get_url_to_report_with_filters\",\n\"evaluate_filters\",\n\"compare\",\n\"get_filter\",\n\"make_filter_tuple\",\n\"make_filter_dict\",\n\"sanitize_column\",\n\"scrub_urls\",\n\"expand_relative_urls\",\n\"quoted\",\n\"quote_urls\",\n\"unique\",\n\"strip\",\n\"to_markdown\",\n\"md_to_html\",\n\"markdown\",\n\"is_subset\",\n\"generate_hash\",\n\"formatdate\",\n\"get_user_info_for_avatar\",\n\"get_abbr\"\n)\n", "path": "frappe/utils/safe_exec.py"}]}