| 196 | | <p></p> |
|---|
| | 201 | <p>Every <tt>Unit</tt> has an <tt>ID</tt> property. The default ID property |
|---|
| | 202 | is of type <tt>int</tt>; however, you can override that to whatever type |
|---|
| | 203 | you like. As long as you provide your own IDs for Units, nothing will |
|---|
| | 204 | break--you can memorize and recall Units without problems. However, if |
|---|
| | 205 | you memorize a Unit with an ID of <tt>None</tt>, the Sandbox may attempt |
|---|
| | 206 | to provide an ID for it.</p> |
|---|
| | 207 | |
|---|
| | 208 | <p>The <tt>Unit</tt> base class possesses a <tt>sequencer</tt> attribute |
|---|
| | 209 | to help Sandboxes generate new IDs. The default value is an instance of |
|---|
| | 210 | <tt>UnitSequencerInteger</tt>, which examines all existing Units, finds |
|---|
| | 211 | the 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 |
|---|
| | 214 | raises an error when asked to generate an ID. If your ID's are strings, |
|---|
| | 215 | you'll probably want to make that class' <tt>.sequencer</tt> one of |
|---|
| | 216 | these, and form ID values in your own code.</p> |
|---|
| | 261 | <h4>Forgetting and Repressing</h4> |
|---|
| | 262 | <p>To <i>forget</i> a Unit is to destroy it forever. You have two options |
|---|
| | 263 | for forgetting Units: you can call <tt>Sandbox().forget(unit)</tt> or |
|---|
| | 264 | the simpler version, <tt>Unit().forget()</tt>. Either of these will clear |
|---|
| | 265 | the Unit from the Sandbox' cache, and the Sandbox will tell the appropriate |
|---|
| | 266 | Storage Manager to destroy the stored Unit data. If a Unit has not yet |
|---|
| | 267 | been 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 |
|---|
| | 270 | Sandbox 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 |
|---|
| | 275 | behaviors. If a Unit possesses an <tt>on_forget</tt> method, it will |
|---|
| | 276 | be 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 |
|---|
| | 278 | has been repressed. I'm sure there was a good reason for this |
|---|
| | 279 | disparity, 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 |
|---|
| | 283 | Sandbox caches. In general, a single call to <tt>flush_all()</tt> will do |
|---|
| | 284 | the trick. Notice that flushing calls <tt>repress()</tt> for each Unit in |
|---|
| | 285 | the 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> |
|---|
| | 289 | function. This returns values, rather than Units. Put simply, it returns |
|---|
| | 290 | all distinct values for the given attribute(s) of the Unit class provided. |
|---|
| | 291 | If only one attribute is specified, a list of values will be returned. |
|---|
| | 292 | If more than one attribute is specified, a zipped list will be returned |
|---|
| | 293 | of all distinct existing combinations. Providing an expr argument (an |
|---|
| | 294 | <tt>Expression</tt> object, see below) will filter the set of Units before |
|---|
| | 295 | obtaining distinct values.</p> |
|---|
| | 296 | |
|---|
| | 297 | <p>The <tt>distinct</tt> function can also be used as a <tt>count</tt> |
|---|
| | 298 | function by passing attrs = ['ID']. Sandboxes provide a |
|---|
| | 299 | <tt>count(cls, expr)</tt> method which does just this.</p> |
|---|
| | 300 | |
|---|
| 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: |
|---|
| | 317 | Neat, 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 |
|---|
| | 320 | into an Expression must possess a single positional argument, which will |
|---|
| | 321 | always be bound to a Unit instance. In the example above, it's named 'x', |
|---|
| | 322 | but you can use any name you like. For complete Unit objects residing in |
|---|
| | 323 | memory, this arrangement means that we can simply call Expression.func(Unit), |
|---|
| | 324 | and receive a boolean value indicating whether our Unit "passes the test". |
|---|
| | 325 | Attribute lookups on our 'x' object will apply to Unit Properties for that |
|---|
| | 326 | Unit 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 |
|---|
| | 329 | important aspect</b> of Expressions: any globals or cell references (from |
|---|
| | 330 | closures) in the supplied lambda get <b>bound early</b>. Compare the |
|---|
| | 331 | following disassemblies: |
|---|
| 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. |
|---|
| | 372 | For example, you can construct an Expression like: |
|---|
| | 373 | <pre>logic.Expression(lambda x: x.Size < 3 and x.Date > dejavu.today())</pre> |
|---|
| | 374 | In this example, the <tt>today()</tt> function breaks convention and is |
|---|
| | 375 | actually <b>bound late</b>. That is, if you construct this Expression now |
|---|
| | 376 | and use it six months later, the value of <tt>today()</tt> will change. |
|---|
| | 377 | Storage Managers "know about" these dejavu functions, and can use them |
|---|
| | 378 | to build more appropriate queries. Here are the functions supplied by |
|---|
| | 379 | the <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 |
|---|
| | 431 | own external functions. However, because Storage Managers are unaware |
|---|
| | 432 | of your new functions, they will not be able to optimize their use; |
|---|
| | 433 | instead, they will simply retrieve a larger set of objects from storage, |
|---|
| | 434 | evaluate each one against the function you provide, and return those |
|---|
| | 435 | Units which match your function.</p> |
|---|
| | 436 | |
|---|
| | 437 | <h4>Combining Expressions</h4> |
|---|
| | 438 | <p>Expressions are combinable; by using the "&" operator, the two condition |
|---|
| | 439 | lists 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 |
|---|
| | 444 | logic.Expression(lambda x: (x.Size > 3) and (x.Size <= 15))</pre> |
|---|
| | 445 | The <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 |
|---|
| | 450 | create common types of Expression objects via the <tt>filter</tt> and |
|---|
| | 452 | |
|---|
| | 453 | <p>The <tt>filter(**kwargs)</tt> function produces an Expression by taking |
|---|
| | 454 | the keyword arguments you supply, and rewriting them in lambda form. The |
|---|
| | 455 | only operator allowed is therefore the equals '==' operator. For example: |
|---|
| | 456 | <pre>>>> logic.filter(Type='Cat', Mutation='Atomic') |
|---|
| | 457 | logic.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 |
|---|
| | 461 | form Expressions with dynamic operators. This can come in handy when you |
|---|
| | 462 | are constructing Expressions on the fly from user input. For example, a |
|---|
| | 463 | search page might prompt users for an attribute name, an operator, and an |
|---|
| | 464 | operand (the criteria).</p> |
|---|
| | 465 | |
|---|
| | 466 | <p>Borrowing from <tt>opcode.cmp_op</tt>, the allowed values for our cmp_op |
|---|
| | 467 | argument are as follows:</p> |
|---|
| | 468 | <table> |
|---|
| | 469 | <tr><th>Numeric Value (cmp_op)</th><th>Operator</th></tr> |
|---|
| | 470 | <tr><td>0</td><td><</td></tr> |
|---|
| | 471 | <tr><td>1</td><td><=</td></tr> |
|---|
| | 472 | <tr><td>2</td><td>==</td></tr> |
|---|
| | 473 | <tr><td>3</td><td>!=</td></tr> |
|---|
| | 474 | <tr><td>4</td><td>></td></tr> |
|---|
| | 475 | <tr><td>5</td><td>>=</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') |
|---|
| | 484 | logic.Expression(lambda x: x.Name != 'Mr. Kamikaze')</pre> |
|---|
| | 485 | Although the comparison function only allows a single comparison at a time, |
|---|
| | 486 | the resulting Expressions can be combined with the <tt>&</tt> and <tt>|</tt> |
|---|
| | 487 | operators (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) |
|---|
| | 491 | isn't limited to Dejavu. Feel free to use it in some other framework or |
|---|
| | 492 | script! The only change you may have to make (if you relocate the module |
|---|
| | 493 | outside 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> |
|---|
| | 497 | Python object, not just dejavu <tt>Unit</tt> instances. If you wish to |
|---|
| | 498 | provide additional logic functions (as dejavu does), simply inject them |
|---|
| | 499 | into <tt>logic</tt>'s globals.</p> |
|---|
| | 500 | |
|---|
| | 501 | <p>You may also find the underlying <tt>codewalk</tt> module useful for |
|---|
| | 502 | other purposes on its own. The <tt>Visitor</tt> base class can be very |
|---|
| | 503 | convenient for building bytecode hacks.</p> |
|---|
| | 504 | |
|---|
| | 505 | <p>To make a long story short, Dejavu depends on <tt>logic</tt> throughout, |
|---|
| | 506 | but 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 | |
|---|