source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/packages/dal/pydal/helpers/classes.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 15.8 KB
Line 
1# -*- coding: utf-8 -*-
2import copy
3import marshal
4import struct
5import threading
6import time
7import traceback
8
9from .._compat import (
10    PY2,
11    exists,
12    copyreg,
13    implements_bool,
14    iterkeys,
15    itervalues,
16    iteritems,
17    long,
18)
19from .._compat import to_bytes
20from .._globals import THREAD_LOCAL
21from .serializers import serializers
22
23
24class cachedprop(object):
25    #: a read-only @property that is only evaluated once.
26    def __init__(self, fget, doc=None):
27        self.fget = fget
28        self.__doc__ = doc or fget.__doc__
29        self.__name__ = fget.__name__
30
31    def __get__(self, obj, cls):
32        if obj is None:
33            return self
34        obj.__dict__[self.__name__] = result = self.fget(obj)
35        return result
36
37
38@implements_bool
39class BasicStorage(object):
40    def __init__(self, *args, **kwargs):
41        return self.__dict__.__init__(*args, **kwargs)
42
43    def __getitem__(self, key):
44        return self.__dict__.__getitem__(str(key))
45
46    __setitem__ = object.__setattr__
47
48    def __delitem__(self, key):
49        try:
50            delattr(self, key)
51        except AttributeError:
52            raise KeyError(key)
53
54    def __bool__(self):
55        return len(self.__dict__) > 0
56
57    __iter__ = lambda self: self.__dict__.__iter__()
58
59    __str__ = lambda self: self.__dict__.__str__()
60
61    __repr__ = lambda self: self.__dict__.__repr__()
62
63    has_key = __contains__ = lambda self, key: key in self.__dict__
64
65    def get(self, key, default=None):
66        return self.__dict__.get(key, default)
67
68    def update(self, *args, **kwargs):
69        return self.__dict__.update(*args, **kwargs)
70
71    def keys(self):
72        return self.__dict__.keys()
73
74    def iterkeys(self):
75        return iterkeys(self.__dict__)
76
77    def values(self):
78        return self.__dict__.values()
79
80    def itervalues(self):
81        return itervalues(self.__dict__)
82
83    def items(self):
84        return self.__dict__.items()
85
86    def iteritems(self):
87        return iteritems(self.__dict__)
88
89    pop = lambda self, *args, **kwargs: self.__dict__.pop(*args, **kwargs)
90
91    clear = lambda self, *args, **kwargs: self.__dict__.clear(*args, **kwargs)
92
93    copy = lambda self, *args, **kwargs: self.__dict__.copy(*args, **kwargs)
94
95
96def pickle_basicstorage(s):
97    return BasicStorage, (dict(s),)
98
99
100copyreg.pickle(BasicStorage, pickle_basicstorage)
101
102
103class OpRow(object):
104    __slots__ = ("_table", "_fields", "_values")
105
106    def __init__(self, table):
107        object.__setattr__(self, "_table", table)
108        object.__setattr__(self, "_fields", {})
109        object.__setattr__(self, "_values", {})
110
111    def set_value(self, key, value, field=None):
112        self._values[key] = value
113        self._fields[key] = self._fields.get(key, field or self._table[key])
114
115    def del_value(self, key):
116        del self._values[key]
117        del self._fields[key]
118
119    def __getitem__(self, key):
120        return self._values[key]
121
122    def __setitem__(self, key, value):
123        return self.set_value(key, value)
124
125    def __delitem__(self, key):
126        return self.del_value(key)
127
128    def __getattr__(self, key):
129        try:
130            return self[key]
131        except KeyError:
132            raise AttributeError
133
134    def __setattr__(self, key, value):
135        return self.set_value(key, value)
136
137    def __delattr__(self, key):
138        return self.del_value(key)
139
140    def __iter__(self):
141        return self._values.__iter__()
142
143    def __contains__(self, key):
144        return key in self._values
145
146    def get(self, key, default=None):
147        try:
148            rv = self[key]
149        except KeyError:
150            rv = default
151        return rv
152
153    def keys(self):
154        return self._values.keys()
155
156    def iterkeys(self):
157        return iterkeys(self._values)
158
159    def values(self):
160        return self._values.values()
161
162    def itervalues(self):
163        return itervalues(self._values)
164
165    def items(self):
166        return self._values.items()
167
168    def iteritems(self):
169        return iteritems(self._values)
170
171    def op_values(self):
172        return [(self._fields[key], value) for key, value in iteritems(self._values)]
173
174    def __repr__(self):
175        return "<OpRow %s>" % repr(self._values)
176
177
178class Serializable(object):
179    def as_dict(self, flat=False, sanitize=True):
180        return self.__dict__
181
182    def as_xml(self, sanitize=True):
183        return serializers.xml(self.as_dict(flat=True, sanitize=sanitize))
184
185    def as_json(self, sanitize=True):
186        return serializers.json(self.as_dict(flat=True, sanitize=sanitize))
187
188    def as_yaml(self, sanitize=True):
189        return serializers.yaml(self.as_dict(flat=True, sanitize=sanitize))
190
191
192class Reference(long):
193    def __allocate(self):
194        if not self._record:
195            self._record = self._table[long(self)]
196        if not self._record:
197            raise RuntimeError(
198                "Using a recursive select but encountered a broken "
199                + "reference: %s %d" % (self._table, long(self))
200            )
201
202    def __getattr__(self, key, default=None):
203        if key == "id":
204            return long(self)
205        if key in self._table:
206            self.__allocate()
207        if self._record:
208            # to deal with case self.update_record()
209            return self._record.get(key, default)
210        else:
211            return None
212
213    def get(self, key, default=None):
214        return self.__getattr__(key, default)
215
216    def __setattr__(self, key, value):
217        if key.startswith("_"):
218            long.__setattr__(self, key, value)
219            return
220        self.__allocate()
221        self._record[key] = value
222
223    def __getitem__(self, key):
224        if key == "id":
225            return long(self)
226        self.__allocate()
227        return self._record.get(key, None)
228
229    def __setitem__(self, key, value):
230        self.__allocate()
231        self._record[key] = value
232
233
234def Reference_unpickler(data):
235    return marshal.loads(data)
236
237
238def Reference_pickler(data):
239    try:
240        marshal_dump = marshal.dumps(long(data))
241    except AttributeError:
242        marshal_dump = "i%s" % struct.pack("<i", long(data))
243    return (Reference_unpickler, (marshal_dump,))
244
245
246copyreg.pickle(Reference, Reference_pickler, Reference_unpickler)
247
248
249class SQLCallableList(list):
250    def __call__(self):
251        return copy.copy(self)
252
253
254class SQLALL(object):
255    """
256    Helper class providing a comma-separated string having all the field names
257    (prefixed by table name and '.')
258
259    normally only called from within gluon.dal
260    """
261
262    def __init__(self, table):
263        self._table = table
264
265    def __str__(self):
266        return ", ".join([str(field) for field in self._table])
267
268
269class SQLCustomType(object):
270    """
271    Allows defining of custom SQL types
272
273    Args:
274        type: the web2py type (default = 'string')
275        native: the backend type
276        encoder: how to encode the value to store it in the backend
277        decoder: how to decode the value retrieved from the backend
278        validator: what validators to use ( default = None, will use the
279            default validator for type)
280
281    Example::
282        Define as:
283
284            decimal = SQLCustomType(
285                type ='double',
286                native ='integer',
287                encoder =(lambda x: int(float(x) * 100)),
288                decoder = (lambda x: Decimal("0.00") + Decimal(str(float(x)/100)) )
289                )
290
291            db.define_table(
292                'example',
293                Field('value', type=decimal)
294                )
295
296    """
297
298    def __init__(
299        self,
300        type="string",
301        native=None,
302        encoder=None,
303        decoder=None,
304        validator=None,
305        _class=None,
306        widget=None,
307        represent=None,
308    ):
309        self.type = type
310        self.native = native
311        self.encoder = encoder or (lambda x: x)
312        self.decoder = decoder or (lambda x: x)
313        self.validator = validator
314        self._class = _class or type
315        self.widget = widget
316        self.represent = represent
317
318    def startswith(self, text=None):
319        try:
320            return self.type.startswith(self, text)
321        except TypeError:
322            return False
323
324    def endswith(self, text=None):
325        try:
326            return self.type.endswith(self, text)
327        except TypeError:
328            return False
329
330    def __getslice__(self, a=0, b=100):
331        return None
332
333    def __getitem__(self, i):
334        return None
335
336    def __str__(self):
337        return self._class
338
339
340class RecordOperator(object):
341    def __init__(self, colset, table, id):
342        self.colset, self.db, self.tablename, self.id = (
343            colset,
344            table._db,
345            table._tablename,
346            id,
347        )
348
349    def __call__(self):
350        pass
351
352
353class RecordUpdater(RecordOperator):
354    def __call__(self, **fields):
355        colset, db, tablename, id = self.colset, self.db, self.tablename, self.id
356        table = db[tablename]
357        newfields = fields or dict(colset)
358        for fieldname in list(newfields.keys()):
359            if fieldname not in table.fields or table[fieldname].type == "id":
360                del newfields[fieldname]
361        table._db(table._id == id, ignore_common_filters=True).update(**newfields)
362        colset.update(newfields)
363        return colset
364
365
366class RecordDeleter(RecordOperator):
367    def __call__(self):
368        return self.db(self.db[self.tablename]._id == self.id).delete()
369
370
371class MethodAdder(object):
372    def __init__(self, table):
373        self.table = table
374
375    def __call__(self):
376        return self.register()
377
378    def __getattr__(self, method_name):
379        return self.register(method_name)
380
381    def register(self, method_name=None):
382        def _decorated(f):
383            instance = self.table
384            import types
385
386            if PY2:
387                method = types.MethodType(f, instance, instance.__class__)
388            else:
389                method = types.MethodType(f, instance)
390            name = method_name or f.func_name
391            setattr(instance, name, method)
392            return f
393
394        return _decorated
395
396
397class FakeCursor(object):
398    """
399    The Python Database API Specification has a cursor() method, which
400    NoSql drivers generally don't support.  If the exception in this
401    function is taken then it likely means that some piece of
402    functionality has not yet been implemented in the driver. And
403    something is using the cursor.
404
405    https://www.python.org/dev/peps/pep-0249/
406    """
407
408    def warn_bad_usage(self, attr):
409        raise Exception("FakeCursor.%s is not implemented" % attr)
410
411    def __getattr__(self, attr):
412        self.warn_bad_usage(attr)
413
414    def __setattr__(self, attr, value):
415        self.warn_bad_usage(attr)
416
417
418class NullCursor(FakeCursor):
419    lastrowid = 1
420
421    def __getattr__(self, attr):
422        return lambda *a, **b: []
423
424
425class FakeDriver(BasicStorage):
426    def __init__(self, *args, **kwargs):
427        super(FakeDriver, self).__init__(*args, **kwargs)
428        self._build_cursor_()
429
430    def _build_cursor_(self):
431        self._fake_cursor_ = FakeCursor()
432
433    def cursor(self):
434        return self._fake_cursor_
435
436    def close(self):
437        return None
438
439    def commit(self):
440        return None
441
442    def __str__(self):
443        state = ["%s=%r" % (attribute, value) for (attribute, value) in self.items()]
444        return "\n".join(state)
445
446
447class NullDriver(FakeDriver):
448    def _build_cursor_(self):
449        self._fake_cursor_ = NullCursor()
450
451
452class ExecutionHandler(object):
453    def __init__(self, adapter):
454        self.adapter = adapter
455
456    def before_execute(self, command):
457        pass
458
459    def after_execute(self, command):
460        pass
461
462
463class TimingHandler(ExecutionHandler):
464    MAXSTORAGE = 100
465
466    def _timings(self):
467        THREAD_LOCAL._pydal_timings_ = getattr(THREAD_LOCAL, "_pydal_timings_", [])
468        return THREAD_LOCAL._pydal_timings_
469
470    @property
471    def timings(self):
472        return self._timings()
473
474    def before_execute(self, command):
475        self.t = time.time()
476
477    def after_execute(self, command):
478        dt = time.time() - self.t
479        self.timings.append((command, dt))
480        del self.timings[: -self.MAXSTORAGE]
481
482
483class DatabaseStoredFile:
484
485    web2py_filesystems = set()
486
487    def escape(self, obj):
488        return self.db._adapter.escape(obj)
489
490    @staticmethod
491    def try_create_web2py_filesystem(db):
492        if db._uri not in DatabaseStoredFile.web2py_filesystems:
493            if db._adapter.dbengine not in ("mysql", "postgres", "sqlite"):
494                raise NotImplementedError(
495                    "DatabaseStoredFile only supported by mysql, potresql, sqlite"
496                )
497            sql = "CREATE TABLE IF NOT EXISTS web2py_filesystem (path VARCHAR(255), content BLOB, PRIMARY KEY(path));"
498            if db._adapter.dbengine == "mysql":
499                sql = sql[:-1] + " ENGINE=InnoDB;"
500            db.executesql(sql)
501            DatabaseStoredFile.web2py_filesystems.add(db._uri)
502
503    def __init__(self, db, filename, mode):
504        if db._adapter.dbengine not in ("mysql", "postgres", "sqlite"):
505            raise RuntimeError(
506                "only MySQL/Postgres/SQLite can store metadata .table files"
507                + " in database for now"
508            )
509        self.db = db
510        self.filename = filename
511        self.mode = mode
512        DatabaseStoredFile.try_create_web2py_filesystem(db)
513        self.p = 0
514        self.data = b""
515        if mode in ("r", "rw", "rb", "a", "ab"):
516            query = "SELECT content FROM web2py_filesystem WHERE path='%s'" % filename
517            rows = self.db.executesql(query)
518            if rows:
519                self.data = to_bytes(rows[0][0])
520            elif exists(filename):
521                datafile = open(filename, "rb")
522                try:
523                    self.data = datafile.read()
524                finally:
525                    datafile.close()
526            elif mode in ("r", "rw", "rb"):
527                raise RuntimeError("File %s does not exist" % filename)
528
529    def read(self, bytes=None):
530        if bytes is None:
531            bytes = len(self.data)
532        data = self.data[self.p : self.p + bytes]
533        self.p += len(data)
534        return data
535
536    def readinto(self, bytes):
537        return self.read(bytes)
538
539    def readline(self):
540        i = self.data.find("\n", self.p) + 1
541        if i > 0:
542            data, self.p = self.data[self.p : i], i
543        else:
544            data, self.p = self.data[self.p :], len(self.data)
545        return data
546
547    def write(self, data):
548        self.data += data
549
550    def close_connection(self):
551        if self.db is not None:
552            self.db.executesql(
553                "DELETE FROM web2py_filesystem WHERE path='%s'" % self.filename
554            )
555            query = "INSERT INTO web2py_filesystem(path,content) VALUES ('%s','%s')"
556            args = (to_bytes(self.filename), self.data)
557            self.db.executesql(query, args)
558            self.db.commit()
559            self.db = None
560
561    def close(self):
562        self.close_connection()
563
564    @staticmethod
565    def is_operational_error(db, error):
566        if not hasattr(db._adapter.driver, "OperationalError"):
567            return None
568        return isinstance(error, db._adapter.driver.OperationalError)
569
570    @staticmethod
571    def is_programming_error(db, error):
572        if not hasattr(db._adapter.driver, "ProgrammingError"):
573            return None
574        return isinstance(error, db._adapter.driver.ProgrammingError)
575
576    @staticmethod
577    def exists(db, filename):
578        if exists(filename):
579            return True
580
581        DatabaseStoredFile.try_create_web2py_filesystem(db)
582
583        query = "SELECT path FROM web2py_filesystem WHERE path='%s'" % filename
584        try:
585            if db.executesql(query):
586                return True
587        except Exception as e:
588            if not (
589                DatabaseStoredFile.is_operational_error(db, e)
590                or DatabaseStoredFile.is_programming_error(db, e)
591            ):
592                raise
593            # no web2py_filesystem found?
594            tb = traceback.format_exc()
595            db.logger.error("Could not retrieve %s\n%s" % (filename, tb))
596        return False
Note: See TracBrowser for help on using the repository browser.