You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-lib/pym/calculate/contrib/spyne/store/relational/document.py

354 lines
12 KiB

#
# spyne - Copyright (C) Spyne contributors.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
#
import logging
logger = logging.getLogger(__name__)
import os
import json
import shutil
import sqlalchemy.dialects
from uuid import uuid1
from mmap import mmap, ACCESS_READ
from contextlib import closing
from os.path import join, abspath, dirname, basename, isfile
try:
from lxml import etree
from lxml import html
from spyne.util.xml import get_object_as_xml, get_xml_as_object
except ImportError as _import_error:
etree = None
html = None
_local_import_error = _import_error
def get_object_as_xml(*_, **__):
raise _local_import_error
def get_xml_as_object(*_, **__):
raise _local_import_error
from sqlalchemy.sql.type_api import UserDefinedType
from spyne import ValidationError
from spyne.model.relational import FileData
from spyne.util import six
from spyne.util.six import binary_type, text_type, BytesIO, StringIO
from spyne.util.fileproxy import SeekableFileProxy
class PGXml(UserDefinedType):
def __init__(self, pretty_print=False, xml_declaration=False,
encoding='UTF-8'):
super(PGXml, self).__init__()
self.xml_declaration = xml_declaration
self.pretty_print = pretty_print
self.encoding = encoding
def get_col_spec(self, **_):
return "xml"
def bind_processor(self, dialect):
def process(value):
if value is None or \
isinstance(value, (six.text_type, six.binary_type)):
return value
if six.PY2:
return etree.tostring(value, pretty_print=self.pretty_print,
encoding=self.encoding, xml_declaration=False)
return etree.tostring(value, pretty_print=self.pretty_print,
encoding="unicode", xml_declaration=False)
return process
def result_processor(self, dialect, col_type):
def process(value):
if value is not None:
return etree.fromstring(value)
else:
return value
return process
sqlalchemy.dialects.postgresql.base.ischema_names['xml'] = PGXml
class PGHtml(UserDefinedType):
def __init__(self, pretty_print=False, encoding='UTF-8'):
super(PGHtml, self).__init__()
self.pretty_print = pretty_print
self.encoding = encoding
def get_col_spec(self, **_):
return "text"
def bind_processor(self, dialect):
def process(value):
if isinstance(value, (six.text_type, six.binary_type)) \
or value is None:
return value
else:
return html.tostring(value, pretty_print=self.pretty_print,
encoding=self.encoding)
return process
def result_processor(self, dialect, col_type):
def process(value):
if value is not None and len(value) > 0:
return html.fromstring(value)
else:
return None
return process
class PGJson(UserDefinedType):
def __init__(self, encoding='UTF-8'):
self.encoding = encoding
def get_col_spec(self, **_):
return "json"
def bind_processor(self, dialect):
def process(value):
if isinstance(value, (text_type, binary_type)) or value is None:
return value
else:
if six.PY2:
return json.dumps(value, encoding=self.encoding)
else:
return json.dumps(value)
return process
def result_processor(self, dialect, col_type):
def process(value):
if isinstance(value, (text_type, binary_type)):
return json.loads(value)
else:
return value
return process
sqlalchemy.dialects.postgresql.base.ischema_names['json'] = PGJson
class PGJsonB(PGJson):
def get_col_spec(self, **_):
return "jsonb"
sqlalchemy.dialects.postgresql.base.ischema_names['jsonb'] = PGJsonB
class PGObjectXml(UserDefinedType):
def __init__(self, cls, root_tag_name=None, no_namespace=False,
pretty_print=False):
self.cls = cls
self.root_tag_name = root_tag_name
self.no_namespace = no_namespace
self.pretty_print = pretty_print
def get_col_spec(self, **_):
return "xml"
def bind_processor(self, dialect):
def process(value):
if value is not None:
return etree.tostring(get_object_as_xml(value, self.cls,
self.root_tag_name, self.no_namespace), encoding='utf8',
pretty_print=self.pretty_print, xml_declaration=False)
return process
def result_processor(self, dialect, col_type):
def process(value):
if value is not None:
return get_xml_as_object(etree.fromstring(value), self.cls)
return process
class PGObjectJson(UserDefinedType):
def __init__(self, cls, ignore_wrappers=True, complex_as=dict, dbt='json',
encoding='utf8'):
self.cls = cls
self.ignore_wrappers = ignore_wrappers
self.complex_as = complex_as
self.dbt = dbt
self.encoding = encoding
from spyne.util.dictdoc import get_dict_as_object
from spyne.util.dictdoc import get_object_as_json
self.get_object_as_json = get_object_as_json
self.get_dict_as_object = get_dict_as_object
def get_col_spec(self, **_):
return self.dbt
def bind_processor(self, dialect):
def process(value):
if value is not None:
try:
return self.get_object_as_json(value, self.cls,
ignore_wrappers=self.ignore_wrappers,
complex_as=self.complex_as,
).decode(self.encoding)
except Exception as e:
logger.debug("Failed to serialize %r to json: %r", value, e)
raise
return process
def result_processor(self, dialect, col_type):
from spyne.util.dictdoc import JsonDocument
def process(value):
if value is None:
return None
if isinstance(value, six.binary_type):
value = value.decode(self.encoding)
if isinstance(value, six.text_type):
return self.get_dict_as_object(json.loads(value), self.cls,
ignore_wrappers=self.ignore_wrappers,
complex_as=self.complex_as,
protocol=JsonDocument,
)
return self.get_dict_as_object(value, self.cls,
ignore_wrappers=self.ignore_wrappers,
complex_as=self.complex_as,
protocol=JsonDocument,
)
return process
class PGFileJson(PGObjectJson):
def __init__(self, store, type=None, dbt='json'):
if type is None:
type = FileData
super(PGFileJson, self).__init__(type, ignore_wrappers=True,
complex_as=list, dbt=dbt)
self.store = store
def bind_processor(self, dialect):
def process(value):
if value is not None:
if value.data is not None:
value.path = uuid1().hex
fp = join(self.store, value.path)
if not abspath(fp).startswith(self.store):
raise ValidationError(value.path, "Path %r contains "
"relative path operators (e.g. '..')")
with open(fp, 'wb') as file:
for d in value.data:
file.write(d)
elif value.handle is not None:
value.path = uuid1().hex
fp = join(self.store, value.path)
if not abspath(fp).startswith(self.store):
raise ValidationError(value.path, "Path %r contains "
"relative path operators (e.g. '..')")
if isinstance(value.handle, (StringIO, BytesIO)):
with open(fp, 'wb') as out_file:
out_file.write(value.handle.getvalue())
else:
with closing(mmap(value.handle.fileno(), 0,
access=ACCESS_READ)) as data:
with open(fp, 'wb') as out_file:
out_file.write(data)
elif value.path is not None:
in_file_path = value.path
if not isfile(in_file_path):
logger.error("File path in %r not found" % value)
if dirname(abspath(in_file_path)) != self.store:
dest = join(self.store, uuid1().get_hex())
if value.move:
shutil.move(in_file_path, dest)
logger.debug("move '%s' => '%s'",
in_file_path, dest)
else:
shutil.copy(in_file_path, dest)
logger.debug("copy '%s' => '%s'",
in_file_path, dest)
value.path = basename(dest)
value.abspath = dest
else:
raise ValueError("Invalid file object passed in. All of "
".data, .handle and .path are None.")
value.store = self.store
value.abspath = join(self.store, value.path)
return self.get_object_as_json(value, self.cls,
ignore_wrappers=self.ignore_wrappers,
complex_as=self.complex_as,
)
return process
def result_processor(self, dialect, col_type):
def process(value):
if value is None:
return None
if isinstance(value, six.text_type):
value = json.loads(value)
elif isinstance(value, six.binary_type):
value = json.loads(value.decode('utf8'))
retval = self.get_dict_as_object(value, self.cls,
ignore_wrappers=self.ignore_wrappers,
complex_as=self.complex_as)
retval.store = self.store
retval.abspath = path = join(self.store, retval.path)
retval.handle = None
retval.data = [b'']
if not os.access(path, os.R_OK):
logger.error("File %r is not readable", path)
return retval
h = retval.handle = SeekableFileProxy(open(path, 'rb'))
if os.fstat(retval.handle.fileno()).st_size > 0:
h.mmap = mmap(h.fileno(), 0, access=ACCESS_READ)
retval.data = (h.mmap,)
# FIXME: Where do we close this mmap?
return retval
return process