/usr/lib/python2.7/dist-packages/elixir/properties.py is in python-elixir 0.7.1-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | '''
This module provides support for defining properties on your entities. It both
provides, the `Property` class which acts as a building block for common
properties such as fields and relationships (for those, please consult the
corresponding modules), but also provides some more specialized properties,
such as `ColumnProperty` and `Synonym`. It also provides the GenericProperty
class which allows you to wrap any SQLAlchemy property, and its DSL-syntax
equivalent: has_property_.
`has_property`
--------------
The ``has_property`` statement allows you to define properties which rely on
their entity's table (and columns) being defined before they can be declared
themselves. The `has_property` statement takes two arguments: first the name of
the property to be defined and second a function (often given as an anonymous
lambda) taking one argument and returning the desired SQLAlchemy property. That
function will be called whenever the entity table is completely defined, and
will be given the .c attribute of the entity as argument (as a way to access
the entity columns).
Here is a quick example of how to use ``has_property``.
.. sourcecode:: python
class OrderLine(Entity):
has_field('quantity', Float)
has_field('unit_price', Float)
has_property('price',
lambda c: column_property(
(c.quantity * c.unit_price).label('price')))
'''
from elixir.statements import PropertyStatement
from sqlalchemy.orm import column_property, synonym
__doc_all__ = ['EntityBuilder', 'Property', 'GenericProperty',
'ColumnProperty']
class EntityBuilder(object):
'''
Abstract base class for all entity builders. An Entity builder is a class
of objects which can be added to an Entity (usually by using special
properties or statements) to "build" that entity. Building an entity,
meaning to add columns to its "main" table, create other tables, add
properties to its mapper, ... To do so an EntityBuilder must override the
corresponding method(s). This is to ensure the different operations happen
in the correct order (for example, that the table is fully created before
the mapper that use it is defined).
'''
def create_pk_cols(self):
pass
def create_non_pk_cols(self):
pass
def before_table(self):
pass
def create_tables(self):
'''
Subclasses may override this method to create tables.
'''
def after_table(self):
pass
def create_properties(self):
'''
Subclasses may override this method to add properties to the involved
entity.
'''
def before_mapper(self):
pass
def after_mapper(self):
pass
def finalize(self):
pass
# helper methods
def add_table_column(self, column):
self.entity._descriptor.add_column(column)
def add_mapper_property(self, name, prop):
self.entity._descriptor.add_property(name, prop)
def add_mapper_extension(self, ext):
self.entity._descriptor.add_mapper_extension(ext)
class CounterMeta(type):
'''
A simple meta class which adds a ``_counter`` attribute to the instances of
the classes it is used on. This counter is simply incremented for each new
instance.
'''
counter = 0
def __call__(self, *args, **kwargs):
instance = type.__call__(self, *args, **kwargs)
instance._counter = CounterMeta.counter
CounterMeta.counter += 1
return instance
class Property(EntityBuilder):
'''
Abstract base class for all properties of an Entity.
'''
__metaclass__ = CounterMeta
def __init__(self, *args, **kwargs):
self.entity = None
self.name = None
def attach(self, entity, name):
"""Attach this property to its entity, using 'name' as name.
Properties will be attached in the order they were declared.
"""
self.entity = entity
self.name = name
# register this property as a builder
entity._descriptor.builders.append(self)
def __repr__(self):
return "Property(%s, %s)" % (self.name, self.entity)
class GenericProperty(Property):
'''
Generic catch-all class to wrap an SQLAlchemy property.
.. sourcecode:: python
class OrderLine(Entity):
quantity = Field(Float)
unit_price = Field(Numeric)
price = GenericProperty(lambda c: column_property(
(c.quantity * c.unit_price).label('price')))
'''
def __init__(self, prop, *args, **kwargs):
super(GenericProperty, self).__init__(*args, **kwargs)
self.prop = prop
#XXX: move this to Property?
self.args = args
self.kwargs = kwargs
def create_properties(self):
if hasattr(self.prop, '__call__'):
prop_value = self.prop(self.entity.table.c)
else:
prop_value = self.prop
prop_value = self.evaluate_property(prop_value)
self.add_mapper_property(self.name, prop_value)
def evaluate_property(self, prop):
if self.args or self.kwargs:
raise Exception('superfluous arguments passed to GenericProperty')
return prop
class ColumnProperty(GenericProperty):
'''
A specialized form of the GenericProperty to generate SQLAlchemy
``column_property``'s.
It takes a function (often given as an anonymous lambda) as its first
argument. Other arguments and keyword arguments are forwarded to the
column_property construct. That first-argument function must accept exactly
one argument and must return the desired (scalar-returning) SQLAlchemy
ClauseElement.
The function will be called whenever the entity table is completely
defined, and will be given
the .c attribute of the table of the entity as argument (as a way to
access the entity columns). The ColumnProperty will first wrap your
ClauseElement in an
"empty" label (ie it will be labelled automatically during queries),
then wrap that in a column_property.
.. sourcecode:: python
class OrderLine(Entity):
quantity = Field(Float)
unit_price = Field(Numeric)
price = ColumnProperty(lambda c: c.quantity * c.unit_price,
deferred=True)
Please look at the `corresponding SQLAlchemy
documentation <http://www.sqlalchemy.org/docs/05/mappers.html
#sql-expressions-as-mapped-attributes>`_ for details.
'''
def evaluate_property(self, prop):
return column_property(prop.label(None), *self.args, **self.kwargs)
class Synonym(GenericProperty):
'''
This class represents a synonym property of another property (column, ...)
of an entity. As opposed to the `synonym` kwarg to the Field class (which
share the same goal), this class can be used to define a synonym of a
property defined in a parent class (of the current class). On the other
hand, it cannot define a synonym for the purpose of using a standard python
property in queries. See the Field class for details on that usage.
.. sourcecode:: python
class Person(Entity):
name = Field(String(30))
primary_email = Field(String(100))
email_address = Synonym('primary_email')
class User(Person):
user_name = Synonym('name')
password = Field(String(20))
'''
def evaluate_property(self, prop):
return synonym(prop, *self.args, **self.kwargs)
#class Composite(GenericProperty):
# def __init__(self, prop):
# super(GenericProperty, self).__init__()
# self.prop = prop
# def evaluate_property(self, prop):
# return composite(prop.label(self.name))
#start = Composite(Point, lambda c: (c.x1, c.y1))
#mapper(Vertex, vertices, properties={
# 'start':composite(Point, vertices.c.x1, vertices.c.y1),
# 'end':composite(Point, vertices.c.x2, vertices.c.y2)
#})
has_property = PropertyStatement(GenericProperty)
|