'''marshmallow fields related to validating input and output file paths'''
import os
import marshmallow as mm
import tempfile
import errno
import sys
import uuid
import stat
import warnings
[docs]class WindowsNamedTemporaryFile():
def __init__(self, dir=None, mode=None):
self.filename = os.path.join(dir, str(uuid.uuid4()))
self.mode = mode
def __enter__(self):
self.open_file = open(self.filename, self.mode)
return self.open_file
def __exit__(self, *args):
self.open_file.close()
os.remove(self.filename)
if sys.platform == "win32":
NamedTemporaryFile = WindowsNamedTemporaryFile
else:
NamedTemporaryFile = tempfile.NamedTemporaryFile
[docs]def validate_outpath(path):
try:
with NamedTemporaryFile(mode='w', dir=path) as tfile:
tfile.write('0')
tfile.close()
except Exception as e:
if isinstance(e, OSError):
if e.errno == errno.ENOENT:
raise mm.ValidationError(
"%s is not in a directory that exists" % path)
elif e.errno == errno.EACCES:
raise mm.ValidationError(
"%s does not appear you can write to path" % path)
else:
raise mm.ValidationError(
"Unknown OSError: {}".format(e.message))
else:
raise mm.ValidationError(
"Unknown Exception: {}".format(e.message))
[docs]class OutputFile(mm.fields.Str):
"""OutputFile :class:`marshmallow.fields.Str` subclass which is a path to a
file location that can be written to by the current user
(presently tested by opening a temporary file to that
location)
Parameters
----------
Returns
-------
"""
def _validate(self, value):
"""
Parameters
----------
value : str
filepath to validate you can write to that location
Returns
-------
None
Raises
------
marshmallow.ValidationError
If os.path.dirname cannot be applied, or if directory does not exist, or if you cannot write to that directory,
or writing a temporary file there produces any crazy exception
"""
try:
path = os.path.dirname(value)
except Exception as e: # pragma: no cover
raise mm.ValidationError(
"%s cannot be os.path.dirname-ed" % value) # pragma: no cover
validate_outpath(path)
[docs]class OutputDirModeException(Exception):
pass
[docs]class OutputDir(mm.fields.Str):
"""OutputDir is a :class:`marshmallow.fields.Str` subclass which is a path to
a location where this module will write files. Validation will check that
the directory exists and create the directory if it is not present,
and will fail validation if the directory cannot be created or cannot be
written to.
Parameters
==========
mode: str
mode to create directory
*args:
smae as passed to marshmallow.fields.Str
**kwargs:
same as passed to marshmallow.fields.Str
"""
def __init__(self, mode=None, *args, **kwargs):
self.mode = mode
if (self.mode is not None) & (sys.platform == "win32"):
raise OutputDirModeException(
"Setting mode of OutputDir supported only on posix systems")
super(OutputDir, self).__init__(*args, **kwargs)
def _validate(self, value):
if not os.path.isdir(value):
try:
os.makedirs(value)
if self.mode is not None:
os.chmod(value, self.mode)
except OSError as e:
if e.errno == errno.EEXIST:
pass
else:
raise mm.ValidationError(
"{} is not a directory and you cannot create it".format(
value)
)
if self.mode is not None:
try:
assert((os.stat(value).st_mode & 0o777) == self.mode)
except AssertionError:
raise mm.ValidationError(
"{} does not have the mode ({}) that was specified ".format(
value, self.mode)
)
except os.error:
raise mm.ValidationError(
"cannot get os.stat of {}".format(value)
)
# use outputfile to test that a file in this location is a valid path
validate_outpath(value)