Skip to content

Commit 01fa872

Browse files
committed
Initial commit
0 parents  commit 01fa872

File tree

7 files changed

+290
-0
lines changed

7 files changed

+290
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*.egg-info
2+
*.egg
3+
*.pyc
4+
.*.swp
5+
.eggs/
6+
.tox/

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (C) 2015 by Hong Minhee <http://hongminhee.org/>
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[bdist_wheel]
2+
universal = 1

setup.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import ast
2+
import os.path
3+
import sys
4+
5+
from setuptools import setup
6+
7+
8+
def get_version():
9+
module_path = os.path.join(os.path.dirname(__file__),
10+
'sqlalchemy_enum34.py')
11+
module_file = open(module_path)
12+
try:
13+
module_code = module_file.read()
14+
finally:
15+
module_file.close()
16+
tree = ast.parse(module_code, module_path)
17+
for node in ast.iter_child_nodes(tree):
18+
if not isinstance(node, ast.Assign) or len(node.targets) != 1:
19+
continue
20+
target, = node.targets
21+
if isinstance(target, ast.Name) and target.id == '__version__':
22+
return node.value.s
23+
24+
25+
def get_install_requires():
26+
install_requires = ['setuptools', 'SQLAlchemy >= 0.8.0']
27+
if 'bdist_wheel' not in sys.argv and sys.version_info < (3, 4):
28+
install_requires.append('enum34')
29+
return install_requires
30+
31+
32+
def get_extras_require():
33+
"""Generate conditional requirements with environ marker."""
34+
for pyversion in '2.5', '2.6', '2.7', '3.2', '3.3':
35+
yield ':python_version==' + repr(pyversion), ['enum34']
36+
37+
38+
setup(
39+
name='SQLAlchemy-Enum34',
40+
description='SQLAlchemy type to store standard enum.Enum value',
41+
version=get_version(),
42+
url='https://github.com/spoqa/sqlalchemy-enum34',
43+
author='Hong Minhee',
44+
author_email='hongminhee' '@' 'member.fsf.org',
45+
license='MIT License',
46+
py_modules=['sqlalchemy_enum34'],
47+
install_requires=get_install_requires(),
48+
extras_require=dict(get_extras_require()),
49+
classifiers=[
50+
'Development Status :: 5 - Production/Stable',
51+
'Intended Audience :: Developers',
52+
'License :: OSI Approved :: MIT License',
53+
'Operating System :: OS Independent',
54+
'Programming Language :: Python :: 2.6',
55+
'Programming Language :: Python :: 2.7',
56+
'Programming Language :: Python :: 3.2',
57+
'Programming Language :: Python :: 3.3',
58+
'Programming Language :: Python :: 3.4',
59+
'Programming Language :: Python :: 3.5',
60+
'Programming Language :: Python :: Implementation :: CPython',
61+
'Programming Language :: Python :: Implementation :: PyPy',
62+
'Programming Language :: Python :: Implementation :: Stackless',
63+
'Programming Language :: SQL',
64+
'Topic :: Database :: Front-Ends',
65+
'Topic :: Software Development',
66+
]
67+
)

sqlalchemy_enum34.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
""":mod:`sqlalchemy_enum34` --- SQLAlchemy :class:`enum` type
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
"""
5+
import enum
6+
7+
from sqlalchemy.types import Enum as BaseEnum, SchemaType, TypeDecorator
8+
9+
__all__ = 'Enum', 'EnumType'
10+
__version__ = '1.0.0'
11+
12+
13+
class Enum(TypeDecorator, SchemaType):
14+
"""Store :class:`enum.Enum` in the standard library (introduced in Python
15+
3.4). Its internal representation is equivalent to SQLAlchemy's built-in
16+
:class:`~sqlalchemy.types.Enum`, but its Python representation is not
17+
a :class:`str` but :class:`enum.Enum`.
18+
19+
:param enum_class: the :class:`enum.Enum` subclass
20+
:type enum_class: :class:`type`
21+
:param by_name: whether to store values by its name instead of its value.
22+
:const:`False` by default
23+
:type by_name: :class:`bool`
24+
:param \*\*options: rest keyword arguments will be passed to
25+
:class:`~sqlalchemy.types.Enum` constructor
26+
27+
"""
28+
29+
impl = BaseEnum
30+
31+
def __init__(self, enum_class, by_name=False, **options):
32+
if not issubclass(enum_class, enum.Enum):
33+
raise TypeError('expected enum.Enum subtype')
34+
if by_name:
35+
enumerants = [m.name for m in enum_class]
36+
else:
37+
enumerants = [m.value for m in enum_class]
38+
super(Enum, self).__init__(*enumerants, **options)
39+
self._enum_class = enum_class
40+
self._by_name = bool(by_name)
41+
42+
def process_bind_param(self, value, dialect):
43+
if self._by_name:
44+
return value.name
45+
return value.value
46+
47+
def process_result_value(self, value, dialect):
48+
if self._by_name:
49+
return self._enum_class[value]
50+
return self._enum_class(value)
51+
52+
def _set_parent(self, column):
53+
self.impl._set_parent(column)
54+
55+
@property
56+
def python_type(self):
57+
return self._enum_class
58+
59+
60+
#: Alias of :class:`Enum`. Can used for disambiguating from
61+
#: :class:`enum.Enum` when their names get duplicated.
62+
EnumType = Enum

test.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import enum
2+
import os
3+
4+
from pytest import fixture, yield_fixture
5+
from sqlalchemy.engine import create_engine
6+
from sqlalchemy.ext.declarative import declarative_base
7+
from sqlalchemy.orm import sessionmaker
8+
from sqlalchemy.pool import NullPool
9+
from sqlalchemy.schema import Column
10+
from sqlalchemy.types import Integer
11+
12+
from sqlalchemy_enum34 import Enum, EnumType
13+
14+
15+
Base = declarative_base()
16+
Session = sessionmaker()
17+
18+
19+
class Color(enum.Enum):
20+
21+
red = 'r'
22+
green = 'g'
23+
blue = 'b'
24+
25+
26+
class ColorTable(Base):
27+
28+
id = Column(Integer, primary_key=True)
29+
color_by_val = Column(Enum(Color, name='color_by_val'))
30+
color_by_name = Column(Enum(Color, by_name=True, name='color_by_name'))
31+
32+
__tablename__ = 'tb_color'
33+
34+
35+
try:
36+
database_urls = os.environ['TEST_DATABASE_URLS'].split()
37+
except KeyError:
38+
database_urls = []
39+
40+
41+
@fixture(scope='function', params=['sqlite://'] + database_urls)
42+
def fx_engine(request):
43+
url = request.param
44+
engine = create_engine(url, poolclass=NullPool)
45+
request.addfinalizer(engine.dispose)
46+
return engine
47+
48+
49+
@yield_fixture
50+
def fx_connection(fx_engine):
51+
connection = fx_engine.connect()
52+
try:
53+
transaction = connection.begin()
54+
try:
55+
metadata = Base.metadata
56+
metadata.create_all(bind=connection)
57+
yield connection
58+
finally:
59+
transaction.rollback()
60+
finally:
61+
connection.close()
62+
63+
64+
@yield_fixture
65+
def fx_session(fx_connection):
66+
session = Session(bind=fx_connection)
67+
try:
68+
yield session
69+
finally:
70+
session.close()
71+
72+
73+
@fixture
74+
def fx_red(fx_session):
75+
red = ColorTable(color_by_val=Color.red, color_by_name=Color.red)
76+
fx_session.add(red)
77+
fx_session.flush()
78+
return red
79+
80+
81+
@fixture
82+
def fx_green(fx_session):
83+
green = ColorTable(color_by_val=Color.green, color_by_name=Color.green)
84+
fx_session.add(green)
85+
fx_session.flush()
86+
return green
87+
88+
89+
@fixture
90+
def fx_blue(fx_session):
91+
blue = ColorTable(color_by_val=Color.blue, color_by_name=Color.blue)
92+
fx_session.add(blue)
93+
fx_session.flush()
94+
return blue
95+
96+
97+
def test_enum_by_value(fx_session, fx_blue, fx_red):
98+
result = fx_session.query(ColorTable) \
99+
.filter_by(color_by_val=Color.blue) \
100+
.one()
101+
assert fx_blue is result
102+
result2 = fx_session.query(ColorTable) \
103+
.filter("tb_color.color_by_val = 'r'") \
104+
.one()
105+
assert fx_red is result2
106+
107+
108+
def test_enum_by_name(fx_session, fx_green, fx_blue):
109+
result = fx_session.query(ColorTable) \
110+
.filter_by(color_by_name=Color.green) \
111+
.one()
112+
assert fx_green is result
113+
result2 = fx_session.query(ColorTable) \
114+
.filter("tb_color.color_by_name = 'blue'") \
115+
.one()
116+
assert fx_blue is result2
117+
118+
119+
def test_enum_is_enum_type():
120+
assert Enum is EnumType

tox.ini

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[tox]
2+
envlist = pypy, py26, py27, py32, pypy3, py33, py34
3+
minversion = 1.6.0
4+
5+
[testenv]
6+
deps =
7+
pytest
8+
flake8
9+
commands =
10+
py.test test.py
11+
flake8 .
12+
13+
[flake8]
14+
filename = sqlalchemy_enum34.py,test*.py

0 commit comments

Comments
 (0)