# Path: cliche/sqltypes.py
# class EnumType(TypeDecorator, SchemaType):
#     """Custom enum type to be used as :class:`enum.Enum`in Python standard
#     library. It inherits :class:`sqlalchemy.types.SchemaType` since it
#     requires schema-level DDL. PostgreSQL ENUM type defined in an Alembic
#     script must be explicitly created/dropped.
#     """
# 
#     impl = Enum
# 
#     def __init__(self, enum_class: enum.Enum, **kw):
#         if not issubclass(enum_class, enum.Enum):
#             raise TypeError('expected enum.Enum subtype')
#         super().__init__(*(m.name for m in enum_class), **kw)
#         self._enum_class = enum_class
# 
#     def process_bind_param(self, value, dialect):
#         return value.name
# 
#     def process_result_value(self, value, dialect):
#         return self._enum_class[value]
# 
#     def _set_parent(self, column):
#         self.impl._set_parent(column)
# 
#     @property
#     def python_type(self):
#         return self._enum_class
# 
# class LocaleType(TypeDecorator):
#     """Custom locale type to be used as :class:`babel.Locale`."""
# 
#     impl = String
# 
#     def bind_processor(self, dialect):
#         def process_bind_param(value):
#             if not issubclass(value.__class__, Locale):
#                 raise TypeError('expected babel.Locale instance')
#             return '{}_{}'.format(value.language, value.territory)
#         return process_bind_param
# 
#     def result_processor(self, dialect, coltype):
#         def process_result_value(value):
#             if value is None:
#                 return value
#             return HashableLocale.parse(value)
#         return process_result_value
# 
#     class comparator_factory(TypeDecorator.Comparator):
#         @property
#         def language(self):
#             return func.substr(self.expr, 1, 2)
# 
#         @property
#         def territory(self):
#             return func.substr(self.expr, 4, 2)
# 
# def prevent_discriminator_from_changing(col):
#     def set_discriminator(target, value, oldvalue, initiator):
#         oldvalue_is_none = (
#             oldvalue.__class__ != _symbol or oldvalue.name != 'NO_VALUE'
#         )
#         value_is_wrong = (
#             value != target.__mapper_args__['polymorphic_identity']
#         )
#         if oldvalue_is_none or value_is_wrong:
#             raise AttributeError('discriminator column cannot be changed')
# 
#     event.listen(col, 'set', set_discriminator)
# 
# def prevent_instantiating(cls):
#     def init_non_instantiable_cls(target, args, kwargs):
#         raise Exception('{} cannot be instantiated'.format(target.__class__))
# 
#     event.listen(cls, 'init', init_non_instantiable_cls)
# 
# class UuidType(TypeDecorator):
#     """Custom UUID type to be used as :class:`uuid.UUID`."""
# 
#     impl = CHAR
# 
#     def load_dialect_impl(self, dialect):
#         if dialect.name == 'postgresql':
#             return dialect.type_descriptor(UUID())
#         else:
#             return dialect.type_descriptor(CHAR(32))
# 
#     def bind_processor(self, dialect):
#         def process_bind_param(value):
#             if value is None:
#                 return value
#             if not isinstance(value, uuid.UUID):
#                 raise TypeError('expected uuid.UUID instance')
# 
#             if dialect.name == 'postgresql':
#                 return str(value)
#             else:
#                 return '{0:032x}'.format(int(value))
#         return process_bind_param
# 
#     def result_processor(self, dialect, coltype):
#         def process_result_value(value):
#             if value is None or isinstance(value, uuid.UUID):
#                 return value
#             else:
#                 return uuid.UUID(value)
#         return process_result_value
# 
# Path: cliche/orm.py
# def make_repr(self):
# def get_alembic_config(engine):
# def initialize_database(engine):
# def get_database_revision(engine):
#     def get_revision(rev, context):
# def upgrade_database(engine, revision='head'):
#     def upgrade(rev, context):
#         def update_current_rev(old, new):
# def downgrade_database(engine, revision):
# def import_all_modules(dry_run=False):  # FIXME


import enum
import uuid
from babel import Locale
from sqlalchemy.schema import Column, ForeignKey
from sqlalchemy.types import Integer, String
from cliche.sqltypes import (EnumType, LocaleType,
                             prevent_discriminator_from_changing,
                             prevent_instantiating, UuidType)
from cliche.orm import Base

def test_enum_type(fx_session):
    red_obj = ColorTable(color=Color.red)
    green_obj = ColorTable(color=Color.green)
    blue_obj = ColorTable(color=Color.blue)
    fx_session.add(red_obj)
    fx_session.add(green_obj)
    fx_session.add(blue_obj)
    fx_session.flush()
    result_obj = fx_session.query(ColorTable) \
                           .filter(ColorTable.color == Color.green) \
                           .one()
    assert green_obj is result_obj


def test_locale_type(fx_session):
    en_us = LocaleTable(locale=Locale.parse('en_US'))
    de_de = LocaleTable(locale=Locale.parse('de_DE'))
    ko_kr = LocaleTable(locale=Locale.parse('ko_KR'))
    with fx_session.begin():
        fx_session.add_all([en_us, de_de, ko_kr])
    result = fx_session.query(LocaleTable) \
                       .filter(LocaleTable.locale == Locale.parse('de_DE')) \
                       .one()
    assert result.locale == Locale.parse('de_DE')


class UuidTable(Base):
    __tablename__ = 'uuid_table'


# Path: cliche/sqltypes.py
# class EnumType(TypeDecorator, SchemaType):
#     """Custom enum type to be used as :class:`enum.Enum`in Python standard
#     library. It inherits :class:`sqlalchemy.types.SchemaType` since it
#     requires schema-level DDL. PostgreSQL ENUM type defined in an Alembic
#     script must be explicitly created/dropped.
#     """
# 
#     impl = Enum
# 
#     def __init__(self, enum_class: enum.Enum, **kw):
#         if not issubclass(enum_class, enum.Enum):
#             raise TypeError('expected enum.Enum subtype')
#         super().__init__(*(m.name for m in enum_class), **kw)
#         self._enum_class = enum_class
# 
#     def process_bind_param(self, value, dialect):
#         return value.name
# 
#     def process_result_value(self, value, dialect):
#         return self._enum_class[value]
# 
#     def _set_parent(self, column):
#         self.impl._set_parent(column)
# 
#     @property
#     def python_type(self):
#         return self._enum_class
# 
# class LocaleType(TypeDecorator):
#     """Custom locale type to be used as :class:`babel.Locale`."""
# 
#     impl = String
# 
#     def bind_processor(self, dialect):
#         def process_bind_param(value):
#             if not issubclass(value.__class__, Locale):
#                 raise TypeError('expected babel.Locale instance')
#             return '{}_{}'.format(value.language, value.territory)
#         return process_bind_param
# 
#     def result_processor(self, dialect, coltype):
#         def process_result_value(value):
#             if value is None:
#                 return value
#             return HashableLocale.parse(value)
#         return process_result_value
# 
#     class comparator_factory(TypeDecorator.Comparator):
#         @property
#         def language(self):
#             return func.substr(self.expr, 1, 2)
# 
#         @property
#         def territory(self):
#             return func.substr(self.expr, 4, 2)
# 
# def prevent_discriminator_from_changing(col):
#     def set_discriminator(target, value, oldvalue, initiator):
#         oldvalue_is_none = (
#             oldvalue.__class__ != _symbol or oldvalue.name != 'NO_VALUE'
#         )
#         value_is_wrong = (
#             value != target.__mapper_args__['polymorphic_identity']
#         )
#         if oldvalue_is_none or value_is_wrong:
#             raise AttributeError('discriminator column cannot be changed')
# 
#     event.listen(col, 'set', set_discriminator)
# 
# def prevent_instantiating(cls):
#     def init_non_instantiable_cls(target, args, kwargs):
#         raise Exception('{} cannot be instantiated'.format(target.__class__))
# 
#     event.listen(cls, 'init', init_non_instantiable_cls)
# 
# class UuidType(TypeDecorator):
#     """Custom UUID type to be used as :class:`uuid.UUID`."""
# 
#     impl = CHAR
# 
#     def load_dialect_impl(self, dialect):
#         if dialect.name == 'postgresql':
#             return dialect.type_descriptor(UUID())
#         else:
#             return dialect.type_descriptor(CHAR(32))
# 
#     def bind_processor(self, dialect):
#         def process_bind_param(value):
#             if value is None:
#                 return value
#             if not isinstance(value, uuid.UUID):
#                 raise TypeError('expected uuid.UUID instance')
# 
#             if dialect.name == 'postgresql':
#                 return str(value)
#             else:
#                 return '{0:032x}'.format(int(value))
#         return process_bind_param
# 
#     def result_processor(self, dialect, coltype):
#         def process_result_value(value):
#             if value is None or isinstance(value, uuid.UUID):
#                 return value
#             else:
#                 return uuid.UUID(value)
#         return process_result_value
# 
# Path: cliche/orm.py
# def make_repr(self):
# def get_alembic_config(engine):
# def initialize_database(engine):
# def get_database_revision(engine):
#     def get_revision(rev, context):
# def upgrade_database(engine, revision='head'):
#     def upgrade(rev, context):
#         def update_current_rev(old, new):
# def downgrade_database(engine, revision):
# def import_all_modules(dry_run=False):  # FIXME


# Path: tests/sqltypes_test.py
import enum
import uuid
from babel import Locale
from sqlalchemy.schema import Column, ForeignKey
from sqlalchemy.types import Integer, String
from cliche.sqltypes import (EnumType, LocaleType,
                             prevent_discriminator_from_changing,
                             prevent_instantiating, UuidType)
from cliche.orm import Base


def test_enum_type(fx_session):
    red_obj = ColorTable(color=Color.red)
    green_obj = ColorTable(color=Color.green)
    blue_obj = ColorTable(color=Color.blue)
    fx_session.add(red_obj)
    fx_session.add(green_obj)
    fx_session.add(blue_obj)
    fx_session.flush()
    result_obj = fx_session.query(ColorTable) \
                           .filter(ColorTable.color == Color.green) \
                           .one()
    assert green_obj is result_obj


def test_locale_type(fx_session):
    en_us = LocaleTable(locale=Locale.parse('en_US'))
    de_de = LocaleTable(locale=Locale.parse('de_DE'))
    ko_kr = LocaleTable(locale=Locale.parse('ko_KR'))
    with fx_session.begin():
        fx_session.add_all([en_us, de_de, ko_kr])
    result = fx_session.query(LocaleTable) \
                       .filter(LocaleTable.locale == Locale.parse('de_DE')) \
                       .one()
    assert result.locale == Locale.parse('de_DE')


class UuidTable(Base):
    __tablename__ = 'uuid_table'


    id = Column(UuidType, primary_key=True, default=uuid.uuid4)