Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

I think I've seen this ORM somewhere before...

Changeset 15

Show
Ignore:
Timestamp:
10/12/04 14:54:25
Author:
fumanchu
Message:

1. Doc updates (modeling.html)
2. Added or_combine to logic.Aggregator, or to logic.Expression

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/__init__.py

    r14 r15  
    646646            return None 
    647647     
    648     def distinct(self, cls, fields, expr=None): 
     648    def distinct(self, cls, attrs, expr=None): 
    649649        """Recall distinct Unit property values. 
    650650         
    651         If only one field is specified, a list of values will be returned. 
    652         If more than one field is specified, a zipped list will be returned. 
     651        If only one attribute is specified, a list of values will be returned. 
     652        If more than one attribute is specified, a zipped list will be returned. 
    653653         
    654654        Notice that you can also use this function as a count() function 
    655         (in fact it's the only way to do it) by using fields = ['ID']. 
     655        (in fact it's the only way to do it) by using attrs = ['ID']. 
    656656        """ 
    657657        seen = {} 
     
    659659        for unit in cache.itervalues(): 
    660660            if expr is None or expr.evaluate(unit): 
    661                 row = tuple([getattr(unit, field) for field in fields]) 
     661                row = tuple([getattr(unit, attr) for attr in attrs]) 
    662662                if row not in seen: 
    663663                    seen[row] = None 
     
    665665        store = self.arena.storage(cls) 
    666666        if store: 
    667             for row in store.distinct(cls, fields, expr): 
     667            for row in store.distinct(cls, attrs, expr): 
    668668                if row not in seen: 
    669669                    seen[row] = None 
     
    671671        seen = seen.keys() 
    672672        seen.sort() 
    673         if len(fields) == 1: 
     673        if len(attrs) == 1: 
    674674            seen = [x[0] for x in seen] 
    675675        return seen 
  • trunk/doc/index.html

    r14 r15  
    2525<li><a href='modeling.html'>Application Developers: Using Dejavu to construct a model</a> 
    2626    <ul> 
    27     <li>Declaring the Unit Clas
     27    <li>Unit
    2828        <ul> 
    2929        <li>UnitProperty</li> 
     30        <li>Creating and Populating Properties</li> 
     31        <li>Unit Properties are First-Class Objects</li> 
     32        <li>Triggers</li> 
     33        <li>Unit ID's</li> 
     34        <li>Registration of Unit Classes</li> 
    3035        </ul> 
    3136    </li> 
    32     <li>Using the Unit Class</li> 
    33     <li>Querying</li> 
    34     <li>Relationships</li> 
    35     <li>Selecting Multiple Objects</li> 
     37    <li>Sandboxes 
     38        <ul> 
     39        <li>Memorizing Units</li> 
     40        <li>Sequencing</li> 
     41        <li>Recalling</li> 
     42        <li>Forgetting and Repressing</li> 
     43        <li>Flushing Sandboxes</li> 
     44        <li>Aggregate Functions</li> 
     45        </ul> 
     46    </li> 
     47    <li>Querying 
     48        <ul> 
     49        <li>The <tt>Expression</tt> class</li> 
     50        <li>External functions within Expressions</li> 
     51        <li>Combining Expressions</li> 
     52        <li>Expressions using <tt>filter</tt> and <tt>comparison</tt></li> 
     53        <li>Exporting the <tt>logic</tt> module</li> 
     54        </ul> 
     55    </li> 
     56    <li>Unit Engines</li> 
     57    <li>Analysis Tools</li> 
     58    <li>The Arena Object 
     59        <ul> 
     60        <li>Unit Class roster</li> 
     61        </ul> 
     62    </li> 
    3663    </ul> 
     64</li> 
    3765<li><a href='storage.html'>Deployers: Configuring Storage</a> 
    3866</li> 
  • trunk/doc/modeling.html

    r14 r15  
    193193not fire.</p> 
    194194 
     195<p>You may define special methods on your Units to provide start-of-life 
     196behaviors. If a Unit possesses an <tt>on_memorize</tt> method, it will 
     197be called after the Unit has been 'reserved' in storage, and after the 
     198Unit has ben placed in the Sandbox cache.</p> 
     199 
    195200<h4>Sequencing</h4> 
    196 <p></p> 
     201<p>Every <tt>Unit</tt> has an <tt>ID</tt> property. The default ID property 
     202is of type <tt>int</tt>; however, you can override that to whatever type 
     203you like. As long as you provide your own IDs for Units, nothing will 
     204break--you can memorize and recall Units without problems. However, if 
     205you memorize a Unit with an ID of <tt>None</tt>, the Sandbox may attempt 
     206to provide an ID for it.</p> 
     207 
     208<p>The <tt>Unit</tt> base class possesses a <tt>sequencer</tt> attribute 
     209to help Sandboxes generate new IDs. The default value is an instance of 
     210<tt>UnitSequencerInteger</tt>, which examines all existing Units, finds 
     211the maximum integer ID, adds 1, and uses that value for the new ID.</p> 
     212 
     213<p>The other useful Sequencer is <tt>UnitSequencerNull</tt>, which simply 
     214raises an error when asked to generate an ID. If your ID's are strings, 
     215you'll probably want to make that class' <tt>.sequencer</tt> one of 
     216these, and form ID values in your own code.</p> 
    197217 
    198218<h4>Recalling</h4> 
     
    239259(although the rest are probably loaded into memory).</p> 
    240260 
     261<h4>Forgetting and Repressing</h4> 
     262<p>To <i>forget</i> a Unit is to destroy it forever. You have two options 
     263for forgetting Units: you can call <tt>Sandbox().forget(unit)</tt> or 
     264the simpler version, <tt>Unit().forget()</tt>. Either of these will clear 
     265the Unit from the Sandbox' cache, and the Sandbox will tell the appropriate 
     266Storage Manager to destroy the stored Unit data. If a Unit has not yet 
     267been memorized, you do not need to forget it.</p> 
     268 
     269<p>In some circumstances, you may wish to only clear the Unit from the 
     270Sandbox without destroying it. You can do this by calling either 
     271<tt>Sandbox().repress(unit)</tt> or the simpler version, 
     272<tt>Unit().repress()</tt>.</p> 
     273 
     274<p>You may define special methods on your Units to provide end-of-life 
     275behaviors. If a Unit possesses an <tt>on_forget</tt> method, it will 
     276be called after the Unit has been destroyed. If a Unit possesses an 
     277<tt>on_repress</tt> method, it will be called <i>before</i> the Unit 
     278has been repressed. I'm sure there was a good reason for this 
     279disparity, but I've forgotten (or perhaps repressed) it.</p> 
     280 
     281<h4>Flushing Sandboxes</h4> 
     282<p>When the client connection has closed, you should <i>flush</i> the 
     283Sandbox caches. In general, a single call to <tt>flush_all()</tt> will do 
     284the trick. Notice that flushing calls <tt>repress()</tt> for each Unit in 
     285the Sandbox, and any <tt>on_repress()</tt> triggers will be executed.</p> 
     286 
     287<h4>Aggregate Functions</h4> 
     288<p>Sandboxes also provide a <tt>distinct(cls, attrs, expr=None)</tt> 
     289function. This returns values, rather than Units. Put simply, it returns 
     290all distinct values for the given attribute(s) of the Unit class provided. 
     291If only one attribute is specified, a list of values will be returned. 
     292If more than one attribute is specified, a zipped list will be returned 
     293of all distinct existing combinations. Providing an expr argument (an  
     294<tt>Expression</tt> object, see below) will filter the set of Units before 
     295obtaining distinct values.</p> 
     296 
     297<p>The <tt>distinct</tt> function can also be used as a <tt>count</tt> 
     298function by passing attrs = ['ID']. Sandboxes provide a 
     299<tt>count(cls, expr)</tt> method which does just this.</p> 
     300 
    241301<h3>Querying</h3> 
    242302<p>When you retrieve Units, you often don't want to load the entire set for 
     
    245305the filter you intend. Dejavu actually provides three ways.</p> 
    246306 
    247 <h4>Expressions</h4> 
     307<h4>The <tt>Expression</tt> class</h4> 
    248308<p>Regardless of which technique you use to express your filter, you're 
    249309going to end up with a <tt>logic.Expression</tt> object. You can build 
     
    255315>>> e 
    256316logic.Expression(lambda x: x.Date >= datetime.date(2004, 3, 1))</pre> 
    257 Neat, eh? I worked hard on that __repr__. ;) What is not obvious from the 
    258 above code snippet is perhaps the <b>most important aspect</b> of 
    259 Expressions: any globals or cell references (from closures) in the 
    260 supplied lambda get <b>bound early</b>. Compare the following disassemblies: 
     317Neat, eh? I worked hard on that __repr__. ;)</p> 
     318 
     319<p>It may be obvious, but we'll be explicit, here. The lambda which you pass 
     320into an Expression must possess a single positional argument, which will 
     321always be bound to a Unit instance. In the example above, it's named 'x', 
     322but you can use any name you like. For complete Unit objects residing in 
     323memory, this arrangement means that we can simply call Expression.func(Unit), 
     324and receive a boolean value indicating whether our Unit "passes the test". 
     325Attribute lookups on our 'x' object will apply to Unit Properties for that 
     326Unit object. That is, <tt>x.Date</tt> becomes <tt>Unit.Date</tt>.</p> 
     327 
     328<p>What is not obvious from the above code snippet is perhaps the <b>most 
     329important aspect</b> of Expressions: any globals or cell references (from  
     330closures) in the supplied lambda get <b>bound early</b>. Compare the 
     331following disassemblies: 
    261332<pre>>>> import dis 
    262333>>> dis.dis(f) 
     
    295366globals into the <tt>logic</tt> module. Note that the <tt>logic</tt> module 
    296367already tries to import <tt>datetime</tt>, <tt>fixedpoint</tt> and 
    297 <tt>decimal</tt>.</p 
    298  
    299 <h4>Syntactic Sugar</h4> 
    300 <p>The <tt>logic</tt> module also provides some convenience functions 
    301 for creating Expression objects via the <tt>filter</tt> and 
     368<tt>decimal</tt>.</p> 
     369 
     370<h4>External functions within Expressions</h4> 
     371<p>Dejavu provides additional functions which can be used in Expressions. 
     372For example, you can construct an Expression like: 
     373<pre>logic.Expression(lambda x: x.Size < 3 and x.Date > dejavu.today())</pre> 
     374In this example, the <tt>today()</tt> function breaks convention and is 
     375actually <b>bound late</b>. That is, if you construct this Expression now 
     376and use it six months later, the value of <tt>today()</tt> will change. 
     377Storage Managers "know about" these dejavu functions, and can use them 
     378to build more appropriate queries. Here are the functions supplied by 
     379the <tt>dejavu</tt> module:</p> 
     380 
     381<table> 
     382<tr><th>Function</th><th>Late bound?</th><th>Description</th></tr> 
     383<tr> 
     384    <td><tt>icontains(a, b)</tt></td> 
     385    <td></td> 
     386    <td>Case-insensitive test b in a. Note the operand order.</td> 
     387</tr> 
     388<tr> 
     389    <td><tt>icontainedby(a, b)</tt></td> 
     390    <td></td> 
     391    <td>Case-insensitive test a in b. Note the operand order.</td> 
     392</tr> 
     393<tr> 
     394    <td><tt>istartswith(a, b)</tt></td> 
     395    <td></td> 
     396    <td>True if a starts with b (case-insensitive), False otherwise.</td> 
     397</tr> 
     398<tr> 
     399    <td><tt>iendswith(a, b)</tt></td> 
     400    <td></td> 
     401    <td>True if a ends with b (case-insensitive), False otherwise.</td> 
     402</tr> 
     403<tr> 
     404    <td><tt>ieq(a, b)</tt></td> 
     405    <td></td> 
     406    <td>True if a == b (case-insensitive), False otherwise.</td> 
     407</tr> 
     408<tr> 
     409    <td><tt>year(value)</tt></td> 
     410    <td></td> 
     411    <td>The year attribute of a date. If value is None, return None.</td> 
     412</tr> 
     413<tr> 
     414    <td><tt>now()</tt></td> 
     415    <td>Y</td> 
     416    <td>datetime.datetime.now()</td> 
     417</tr> 
     418<tr> 
     419    <td><tt>today()</tt></td> 
     420    <td>Y</td> 
     421    <td>datetime.date.today()</td> 
     422</tr> 
     423<tr> 
     424    <td><tt>iscurrentweek(value)</tt></td> 
     425    <td>Y</td> 
     426    <td>If value is in the current week, return True, else False.</td> 
     427</tr> 
     428</table> 
     429 
     430<p>It is possible for you, the application developer, to define your 
     431own external functions. However, because Storage Managers are unaware 
     432of your new functions, they will not be able to optimize their use; 
     433instead, they will simply retrieve a larger set of objects from storage, 
     434evaluate each one against the function you provide, and return those 
     435Units which match your function.</p> 
     436 
     437<h4>Combining Expressions</h4> 
     438<p>Expressions are combinable; by using the "&" operator, the two condition 
     439lists are combined with an adjoining logical "and". For example: 
     440<pre>>>> a = logic.Expression(lambda x: x.Size > 3) 
     441>>> b = logic.Expression(lambda x: x.Size <= 15) 
     442>>> c = a & b 
     443>>> c 
     444logic.Expression(lambda x: (x.Size > 3) and (x.Size <= 15))</pre> 
     445The <tt>+</tt> operator works just like the <tt>&</tt> operator. The 
     446<tt>|</tt> operator combines the two Expressions with a logical 'or'.</p> 
     447 
     448<h4>Expressions using <tt>filter</tt> and <tt>comparison</tt></h4> 
     449<p>The <tt>logic</tt> module also provides convenient methods to 
     450create common types of Expression objects via the <tt>filter</tt> and 
    302451<tt>comparison</tt> factory functions.</p> 
     452 
     453<p>The <tt>filter(**kwargs)</tt> function produces an Expression by taking 
     454the keyword arguments you supply, and rewriting them in lambda form. The  
     455only operator allowed is therefore the equals '==' operator. For example: 
     456<pre>>>> logic.filter(Type='Cat', Mutation='Atomic') 
     457logic.Expression(lambda x: (x.Type == 'Cat') and (x.Mutation == 'Atomic'))</pre> 
     458</p> 
     459 
     460<p>The <tt>comparison(attr, cmp_op, criteria)</tt> function allows you to 
     461form Expressions with dynamic operators. This can come in handy when you 
     462are constructing Expressions on the fly from user input. For example, a 
     463search page might prompt users for an attribute name, an operator, and an 
     464operand (the criteria).</p> 
     465 
     466<p>Borrowing from <tt>opcode.cmp_op</tt>, the allowed values for our cmp_op 
     467argument are as follows:</p> 
     468<table> 
     469<tr><th>Numeric Value (cmp_op)</th><th>Operator</th></tr> 
     470<tr><td>0</td><td>&lt;</td></tr> 
     471<tr><td>1</td><td>&lt;=</td></tr> 
     472<tr><td>2</td><td>==</td></tr> 
     473<tr><td>3</td><td>!=</td></tr> 
     474<tr><td>4</td><td>&gt;</td></tr> 
     475<tr><td>5</td><td>&gt;=</td></tr> 
     476<tr><td>6</td><td>in</td></tr> 
     477<tr><td>7</td><td>not in</td></tr> 
     478<tr><td>8</td><td>is</td></tr> 
     479<tr><td>9</td><td>is not</td></tr> 
     480</table> 
     481 
     482<p>Here's an example of using <tt>comparison</tt>: 
     483<pre>>>> logic.comparison('Name', 3, 'Mr. Kamikaze') 
     484logic.Expression(lambda x: x.Name != 'Mr. Kamikaze')</pre> 
     485Although the comparison function only allows a single comparison at a time, 
     486the resulting Expressions can be combined with the <tt>&</tt> and <tt>|</tt> 
     487operators (described earlier) to produce more complex Expressions.</p> 
     488 
     489<h4>Exporting the <tt>logic</tt> module</h4> 
     490<p>The <tt>logic</tt> module (and <tt>codewalk</tt>, on which it is built) 
     491isn't limited to Dejavu. Feel free to use it in some other framework or 
     492script! The only change you may have to make (if you relocate the module 
     493outside of the <tt>dejavu</tt> package) would be to the single line: 
     494<tt>from dejavu import codewalk</tt>, to point to the new location.</p> 
     495 
     496<p>In particular, <tt>logic.Expression</tt> objects can operate on <i>any</i> 
     497Python object, not just dejavu <tt>Unit</tt> instances. If you wish to 
     498provide additional logic functions (as dejavu does), simply inject them 
     499into <tt>logic</tt>'s globals.</p> 
     500 
     501<p>You may also find the underlying <tt>codewalk</tt> module useful for 
     502other purposes on its own. The <tt>Visitor</tt> base class can be very 
     503convenient for building bytecode hacks.</p> 
     504 
     505<p>To make a long story short, Dejavu depends on <tt>logic</tt> throughout, 
     506but the reverse is not true.</p> 
     507 
     508 
     509<h3>Unit Engines</h3> 
     510<p></p> 
     511 
     512 
     513<h3>Analysis Tools</h3> 
     514<p></p> 
     515 
    303516 
    304517<h3>The Arena Object</h3> 
     
    314527<p></p> 
    315528 
    316 <h3>Unit Engines</h3> 
    317 <p></p> 
    318  
    319 <h3>Analysis Tools</h3> 
    320 <p></p> 
    321  
    322529<br /> 
    323530 
  • trunk/logic.py

    r14 r15  
    187187        self.instr_index[-1:] = [obj] * (newtarget + 4) 
    188188     
     189    def or_combine(self, obj): 
     190        obj = codewalk.Rewriter(obj) 
     191        bytecode = map(ord, obj.co_code) 
     192        newtarget = len(bytecode) 
     193         
     194        self._bytecode.pop() 
     195        self._bytecode.extend([112, newtarget & 0xFF, newtarget >> 8, 
     196                               1]) 
     197        self._bytecode.extend(bytecode) 
     198        self.instr_index[-1:] = [obj] * (newtarget + 4) 
     199     
    189200    def visit_LOAD_ATTR(self, lo, hi): 
    190201        src = self.instr_index[self.cursor] 
     
    233244        return 'logic.Expression(%s)' % self.code() 
    234245     
    235     def __add__(self, other): 
     246    def __and__(self, other): 
    236247        """Logical-and this Expression with another.""" 
    237248        assert isinstance(other, Expression) 
    238249        ag = Aggregator(self.func) 
    239250        ag.and_combine(other.func) 
     251        agfunc = ag.function() 
     252        return Expression(agfunc) 
     253    __add__ = __and__ 
     254     
     255    def __or__(self, other): 
     256        """Logical-or this Expression with another.""" 
     257        assert isinstance(other, Expression) 
     258        ag = Aggregator(self.func) 
     259        ag.or_combine(other.func) 
    240260        agfunc = ag.function() 
    241261        return Expression(agfunc)