Repository: frappe/frappe

Issue:
UnboundLocalError When Trying To Upload File
![peek 2017-12-27 11-05](https://user-images.githubusercontent.com/818803/34378411-f7ab3d64-eaf5-11e7-8a88-2b471baacbf9.gif)

```(python)
Traceback (most recent call last):
  File "/home/tunde/PycharmProjects/bench-repo/dev/apps/frappe/frappe/handler.py", line 109, in uploadfile
    ret = frappe.utils.file_manager.upload()
  File "/home/tunde/PycharmProjects/bench-repo/dev/apps/frappe/frappe/utils/file_manager.py", line 35, in upload
    file_doc = get_file_doc()
  File "/home/tunde/PycharmProjects/bench-repo/dev/apps/frappe/frappe/utils/file_manager.py", line 70, in get_file_doc
    return file_doc
UnboundLocalError: local variable 'file_doc' referenced before assignment
```


Discussion:
This seems to happen with empty files

Resolving patch:
diff --git a/frappe/utils/file_manager.py b/frappe/utils/file_manager.py
--- a/frappe/utils/file_manager.py
+++ b/frappe/utils/file_manager.py
@@ -13,13 +13,16 @@
 from six.moves.urllib.parse import unquote
 from six import text_type
 
-class MaxFileSizeReachedError(frappe.ValidationError): pass
+
+class MaxFileSizeReachedError(frappe.ValidationError):
+	pass
 
 
 def get_file_url(file_data_name):
 	data = frappe.db.get_value("File", file_data_name, ["file_name", "file_url"], as_dict=True)
 	return data.file_url or data.file_name
 
+
 def upload():
 	# get record details
 	dt = frappe.form_dict.doctype
@@ -53,29 +56,37 @@ def upload():
 		"comment": comment.as_dict() if comment else {}
 	}
 
+
 def get_file_doc(dt=None, dn=None, folder=None, is_private=None):
-	'''returns File object (Document) from given parameters or form_dict'''
+	""" Returns File object (Document) from given parameters or form_dict"""
 	r = frappe.form_dict
 
-	if dt is None: dt = r.doctype
-	if dn is None: dn = r.docname
-	if folder is None: folder = r.folder
-	if is_private is None: is_private = r.is_private
-
-	if r.filedata:
+	if dt is None:
+		dt = r.doctype
+	if dn is None:
+		dn = r.docname
+	if folder is None:
+		folder = r.folder
+	if is_private is None:
+		is_private = r.is_private
+
+	# r.filedata will be '' if file is empty
+	if r.filedata or r.filedata == '':
 		file_doc = save_uploaded(dt, dn, folder, is_private)
 	elif r.file_url:
 		file_doc = save_url(r.file_url, r.filename, dt, dn, folder, is_private)
 
 	return file_doc
 
+
 def save_uploaded(dt, dn, folder, is_private):
 	fname, content = get_uploaded_content()
-	if content:
+	if content or content == '':
 		return save_file(fname, content, dt, dn, folder, is_private=is_private);
 	else:
 		raise Exception
 
+
 def save_url(file_url, filename, dt, dn, folder, is_private):
 	# if not (file_url.startswith("http://") or file_url.startswith("https://")):
 	# 	frappe.msgprint("URL must start with 'http://' or 'https://'")
@@ -94,11 +105,12 @@ def save_url(file_url, filename, dt, dn, folder, is_private):
 	})
 	f.flags.ignore_permissions = True
 	try:
-		f.insert();
+		f.insert()
 	except frappe.DuplicateEntryError:
 		return frappe.get_doc("File", f.duplicate_entry)
 	return f
 
+
 def get_uploaded_content():
 	# should not be unicode when reading a file, hence using frappe.form
 	if 'filedata' in frappe.form_dict:
@@ -111,6 +123,7 @@ def get_uploaded_content():
 		frappe.msgprint(_('No file attached'))
 		return None, None
 
+
 def save_file(fname, content, dt, dn, folder=None, decode=False, is_private=0):
 	if decode:
 		if isinstance(content, text_type):
@@ -151,12 +164,14 @@ def save_file(fname, content, dt, dn, folder=None, decode=False, is_private=0):
 
 	return f
 
+
 def get_file_data_from_hash(content_hash, is_private=0):
 	for name in frappe.db.sql_list("select name from `tabFile` where content_hash=%s and is_private=%s", (content_hash, is_private)):
 		b = frappe.get_doc('File', name)
-		return {k:b.get(k) for k in frappe.get_hooks()['write_file_keys']}
+		return {k: b.get(k) for k in frappe.get_hooks()['write_file_keys']}
 	return False
 
+
 def save_file_on_filesystem(fname, content, content_type=None, is_private=0):
 	fpath = write_file(content, fname, is_private)
 
@@ -170,9 +185,11 @@ def save_file_on_filesystem(fname, content, content_type=None, is_private=0):
 		'file_url': file_url
 	}
 
+
 def get_max_file_size():
 	return conf.get('max_file_size') or 10485760
 
+
 def check_max_file_size(content):
 	max_file_size = get_max_file_size()
 	file_size = len(content)
@@ -184,6 +201,7 @@ def check_max_file_size(content):
 
 	return file_size
 
+
 def write_file(content, fname, is_private=0):
 	"""write file to disk with a random name (to compare)"""
 	file_path = get_files_path(is_private=is_private)
@@ -198,6 +216,7 @@ def write_file(content, fname, is_private=0):
 
 	return get_files_path(fname, is_private=is_private)
 
+
 def remove_all(dt, dn, from_delete=False):
 	"""remove all files in a transaction"""
 	try:
@@ -207,6 +226,7 @@ def remove_all(dt, dn, from_delete=False):
 	except Exception as e:
 		if e.args[0]!=1054: raise # (temp till for patched)
 
+
 def remove_file_by_url(file_url, doctype=None, name=None):
 	if doctype and name:
 		fid = frappe.db.get_value("File", {"file_url": file_url,
@@ -217,6 +237,7 @@ def remove_file_by_url(file_url, doctype=None, name=None):
 	if fid:
 		return remove_file(fid)
 
+
 def remove_file(fid, attached_to_doctype=None, attached_to_name=None, from_delete=False):
 	"""Remove file and File entry"""
 	file_name = None
@@ -240,10 +261,12 @@ def remove_file(fid, attached_to_doctype=None, attached_to_name=None, from_delet
 
 	return comment
 
+
 def delete_file_data_content(doc, only_thumbnail=False):
 	method = get_hook_method('delete_file_data_content', fallback=delete_file_from_filesystem)
 	method(doc, only_thumbnail=only_thumbnail)
 
+
 def delete_file_from_filesystem(doc, only_thumbnail=False):
 	"""Delete file, thumbnail from File document"""
 	if only_thumbnail:
@@ -252,6 +275,7 @@ def delete_file_from_filesystem(doc, only_thumbnail=False):
 		delete_file(doc.file_url)
 		delete_file(doc.thumbnail_url)
 
+
 def delete_file(path):
 	"""Delete file from `public folder`"""
 	if path:
@@ -269,6 +293,7 @@ def delete_file(path):
 		if os.path.exists(path):
 			os.remove(path)
 
+
 def get_file(fname):
 	"""Returns [`file_name`, `content`] for given file name `fname`"""
 	file_path = get_file_path(fname)
@@ -279,6 +304,7 @@ def get_file(fname):
 
 	return [file_path.rsplit("/", 1)[-1], content]
 
+
 def get_file_path(file_name):
 	"""Returns file path from given file name"""
 	f = frappe.db.sql("""select file_url from `tabFile`
@@ -302,11 +328,13 @@ def get_file_path(file_name):
 
 	return file_path
 
+
 def get_content_hash(content):
 	if isinstance(content, text_type):
 		content = content.encode()
 	return hashlib.md5(content).hexdigest()
 
+
 def get_file_name(fname, optional_suffix):
 	# convert to unicode
 	fname = cstr(fname)
@@ -321,6 +349,7 @@ def get_file_name(fname, optional_suffix):
 		return '{partial}{suffix}{extn}'.format(partial=partial, extn=extn, suffix=optional_suffix)
 	return fname
 
+
 @frappe.whitelist()
 def download_file(file_url):
 	"""
@@ -340,12 +369,14 @@ def download_file(file_url):
 	frappe.local.response.filecontent = filedata
 	frappe.local.response.type = "download"
 
+
 def extract_images_from_doc(doc, fieldname):
 	content = doc.get(fieldname)
 	content = extract_images_from_html(doc, content)
 	if frappe.flags.has_dataurl:
 		doc.set(fieldname, content)
 
+
 def extract_images_from_html(doc, content):
 	frappe.flags.has_dataurl = False
 
@@ -379,6 +410,7 @@ def _save_file(match):
 
 	return content
 
+
 def get_random_filename(extn=None, content_type=None):
 	if extn:
 		if not extn.startswith("."):


Test patch: