text: # -*- coding: utf-8 -*-
  
  import types
  from functools import update_wrapper
  from .relations.wrapper import Wrapper
  from .builder import Builder
  from ..query import QueryBuilder
  from .relations import (
      HasOne,
      HasMany,
      HasManyThrough,
      BelongsTo,
      BelongsToMany,
      MorphOne,
      MorphMany,
      MorphTo,
      MorphToMany,
  )
  
  
  class accessor(object):
      def __init__(self, accessor_, attribute=None):
          self.accessor = accessor_
          self.mutator_ = None
          if attribute is not None:
              self.attribute = attribute
          else:
              if isinstance(accessor_, property):
                  self.attribute = accessor_.fget.__name__
              else:
                  self.attribute = self.accessor.__name__
  
          self.expr = accessor_
          if accessor_ is not None:
              update_wrapper(self, accessor_)
  
      def __get__(self, instance, owner):
          if instance is None:
              return self.expr
          else:
              return self.accessor(instance)
  
      def __set__(self, instance, value):
          if self.mutator_ is None:
              return instance.set_attribute(self.attribute, value)
  
          self.mutator_(instance, value)
  
      def mutator(self, f):
          self.mutator_ = f
  
          return mutator(f, self.attribute)
  
  
  class mutator(object):
      def __init__(self, mutator_, attribute=None):
          self.mutator = mutator_
          self.accessor_ = None
          self.attribute = attribute or self.mutator.__name__
  
      def __get__(self, instance, owner):
          if instance is None:
              return self.mutator
          else:
              if self.accessor_ is None:
                  return instance.get_attribute(self.attribute)
  
              return self.accessor_(instance)
  
      def __set__(self, instance, value):
          self.mutator(instance, value)
  
      def accessor(self, f):
          self.accessor_ = f
  
          return accessor(f, self.attribute)
  
  
  class column(object):
      def __init__(self, property_, attribute=None):
          self.property = property_
          self.mutator_ = None
          self.accessor_ = None
          if attribute is not None:
              self.attribute = attribute
          else:
              if isinstance(property_, property):
                  self.attribute = property_.fget.__name__
              else:
                  self.attribute = self.property.__name__
  
      def __get__(self, instance, owner):
          if instance is None:
              return self.mutator_
          else:
              if self.accessor_ is None:
                  return instance.get_attribute(self.attribute)
  
              return self.accessor_(instance)
  
      def __set__(self, instance, value):
          if self.mutator_ is None:
              return instance.set_attribute(self.attribute, value)
  
          self.mutator_(instance, value)
  
      def mutator(self, f):
          self.mutator_ = f
  
          return mutator(f, self.attribute)
  
      def accessor(self, f):
          self.accessor_ = f
  
          return accessor(f, self.attribute)
  
  
  class scope(classmethod):
      """
      Decorator to add local scopes.
      """
  
      def __init__(self, method):
          super(scope, self).__init__(method)
  
          self._method = method
          self._owner = None
  
          update_wrapper(self, method)
  
      def __get__(self, instance, owner, *args, **kwargs):
          if instance:
              self._owner = None
          else:
              self._owner = owner
  
          return self
  
      def __call__(self, *args, **kwargs):
          if not self._owner:
              return self._method(self._owner, *args, **kwargs)
          else:
              return getattr(self._owner.query(), self._method.__name__)(*args, **kwargs)
  
  
  # Relations decorators
  class relation(object):
      """
      Base relation decorator
      """
  
      relation_class = None
  
      def __init__(self, func=None, relation=None):
          self._relation = relation
          self._related = None
          self._conditions = None
  
          self.set_func(func)
  
      def set_func(self, func):
          self.func = func
  
          if self._relation is None:
              if isinstance(func, property):
                  self._relation = func.fget.__name__ if func else None
              else:
                  self._relation = func.__name__ if func else None
  
          self.expr = func
          if func is not None:
              update_wrapper(self, func)
  
      def __get__(self, instance, owner):
          if instance is None:
              return self.expr
  
          if self._relation in instance._relations:
              return instance._relations[self._relation]
  
          self._related = self.func(instance)
          if isinstance(self._related, (Builder, QueryBuilder)):
              # Extra conditions on relation
              self._conditions = self._related
              self._related = self._related.get_model().__class__
  
          relation = self._get(instance)
  
          if self._conditions:
              # Setting extra conditions
              self._set_conditions(relation)
  
          relation = Wrapper(relation)
  
          instance._relations[self._relation] = relation
  
          return relation
  
      def _get(self, instance):
          raise NotImplementedError()
  
      def _set_conditions(self, relation):
          relation.merge_query(self._conditions)
          relation.set_extra_query(self._conditions)
  
      def __call__(self, func):
          self.set_func(func)
  
          return self
  
  
  class has_one(relation):
      """
      Has One relationship decorator
      """
  
      relation_class = HasOne
  
      def __init__(self, foreign_key=None, local_key=None, relation=None):
          if isinstance(foreign_key, (types.FunctionType, types.MethodType)):
              func = foreign_key
              foreign_key = None
          else:
              func = None
  
          self._foreign_key = foreign_key
          self._local_key = local_key
  
          super(has_one, self).__init__(func, relation)
  
      def _get(self, instance):
          return instance.has_one(
              self._related,
              self._foreign_key,
              self._local_key,
              self._relation,
              _wrapped=False,
          )
  
  
  class morph_one(relation):
      """
      Morph One relationship decorator
      """
  
      relation_class = MorphOne
  
      def __init__(
          self, name, type_column=None, id_column=None, local_key=None, relation=None
      ):
          if isinstance(name, (types.FunctionType, types.MethodType)):
              raise RuntimeError("morph_one relation requires a name")
  
          self._name = name
          self._type_column = type_column
          self._id_column = id_column
          self._local_key = local_key
  
          super(morph_one, self).__init__(relation=relation)
  
      def _get(self, instance):
          return instance.morph_one(
              self._related,
              self._name,
              self._type_column,
              self._id_column,
              self._local_key,
              self._relation,
              _wrapped=False,
          )
  
  
  class belongs_to(relation):
      """
      Belongs to relationship decorator
      """
  
      relation_class = BelongsTo
  
      def __init__(self, foreign_key=None, other_key=None, relation=None):
          if isinstance(foreign_key, (types.FunctionType, types.MethodType)):
              func = foreign_key
              foreign_key = None
          else:
              func = None
  
          self._foreign_key = foreign_key
          self._other_key = other_key
  
          super(belongs_to, self).__init__(func, relation)
  
      def _get(self, instance):
          return instance.belongs_to(
              self._related,
              self._foreign_key,
              self._other_key,
              self._relation,
              _wrapped=False,
          )
  
      def _set(self, relation):
          relation._foreign_key = self._foreign_key
          relation._other_key = self._other_key
          relation._relation = self._relation
  
  
  class morph_to(relation):
      """
      Morph To relationship decorator
      """
  
      relation_class = MorphTo
  
      def __init__(self, name=None, type_column=None, id_column=None):
          if isinstance(name, (types.FunctionType, types.MethodType)):
              func = name
              name = None
          else:
              func = None
  
          self._name = name
          self._type_column = type_column
          self._id_column = id_column
  
          super(morph_to, self).__init__(func, name)
  
      def _get(self, instance):
          return instance.morph_to(
              self._relation, self._type_column, self._id_column, _wrapped=False
          )
  
  
  class has_many(relation):
      """
      Has Many relationship decorator
      """
  
      relation_class = HasMany
  
      def __init__(self, foreign_key=None, local_key=None, relation=None):
          if isinstance(foreign_key, (types.FunctionType, types.MethodType)):
              func = foreign_key
              foreign_key = None
          else:
              func = None
  
          self._foreign_key = foreign_key
          self._local_key = local_key
  
          super(has_many, self).__init__(func, relation)
  
      def _get(self, instance):
          return instance.has_many(
              self._related,
              self._foreign_key,
              self._local_key,
              self._relation,
              _wrapped=False,
          )
  
  
  class has_many_through(relation):
      """
      Has Many Through relationship decorator
      """
  
      relation_class = HasManyThrough
  
      def __init__(self, through, first_key=None, second_key=None, relation=None):
          if isinstance(through, (types.FunctionType, types.MethodType)):
              raise RuntimeError(
                  "has_many_through relation requires the through parameter"
              )
  
          self._through = through
          self._first_key = first_key
          self._second_key = second_key
  
          super(has_many_through, self).__init__(relation=relation)
  
      def _get(self, instance):
          return instance.has_many_through(
              self._related,
              self._through,
              self._first_key,
              self._second_key,
              self._relation,
              _wrapped=False,
          )
  
  
  class morph_many(relation):
      """
      Morph Many relationship decorator
      """
  
      relation_class = MorphMany
  
      def __init__(
          self, name, type_column=None, id_column=None, local_key=None, relation=None
      ):
          if isinstance(name, (types.FunctionType, types.MethodType)):
              raise RuntimeError("morph_many relation requires a name")
  
          self._name = name
          self._type_column = type_column
          self._id_column = id_column
          self._local_key = local_key
  
          super(morph_many, self).__init__(relation=relation)
  
      def _get(self, instance):
          return instance.morph_many(
              self._related,
              self._name,
              self._type_column,
              self._id_column,
              self._local_key,
              self._relation,
              _wrapped=False,
          )
  
  
  class belongs_to_many(relation):
      """
      Belongs To Many relationship decorator
      """
  
      relation_class = BelongsToMany
  
      def __init__(
          self,
          table=None,
          foreign_key=None,
          other_key=None,
          relation=None,
          with_timestamps=False,
          with_pivot=None,
      ):
          if isinstance(table, (types.FunctionType, types.MethodType)):
              func = table
              table = None
          else:
              func = None
  
          self._table = table
          self._foreign_key = foreign_key
          self._other_key = other_key
  
          self._timestamps = with_timestamps
          self._pivot = with_pivot
  
          super(belongs_to_many, self).__init__(func, relation)
  
      def _get(self, instance):
          r = instance.belongs_to_many(
              self._related,
              self._table,
              self._foreign_key,
              self._other_key,
              self._relation,
              _wrapped=False,
          )
  
          if self._timestamps:
              r = r.with_timestamps()
  
          if self._pivot:
              r = r.with_pivot(*self._pivot)
  
          return r
  
  
  class morph_to_many(relation):
      """
      Morph To Many relationship decorator
      """
  
      relation_class = MorphToMany
  
      def __init__(
          self, name, table=None, foreign_key=None, other_key=None, relation=None
      ):
          if isinstance(name, (types.FunctionType, types.MethodType)):
              raise RuntimeError("morph_to_many relation required a name")
  
          self._name = name
          self._table = table
          self._foreign_key = foreign_key
          self._other_key = other_key
  
          super(morph_to_many, self).__init__(relation=relation)
  
      def _get(self, instance):
          return instance.morph_to_many(
              self._related,
              self._name,
              self._table,
              self._foreign_key,
              self._other_key,
              relation=self._relation,
              _wrapped=False,
          )
  
  
  class morphed_by_many(relation):
      """
      Morphed By Many relationship decorator
      """
  
      relation_class = MorphToMany
  
      def __init__(
          self, name, table=None, foreign_key=None, other_key=None, relation=None
      ):
          if isinstance(foreign_key, (types.FunctionType, types.MethodType)):
              raise RuntimeError("morphed_by_many relation requires a name")
  
          self._name = name
          self._table = table
          self._foreign_key = foreign_key
          self._other_key = other_key
  
          super(morphed_by_many, self).__init__(relation=relation)
  
      def _get(self, instance):
          return instance.morphed_by_many(
              self._related,
              self._name,
              self._table,
              self._foreign_key,
              self._other_key,
              self._relation,
              _wrapped=False,
          )
  