Changeset 364

Show
Ignore:
Timestamp:
01/15/07 09:53:36 (2 years ago)
Author:
athomas
Message:

pyndexter:

  • Re-added Hype adapter as it seems I was mistaken about its demise (closes #33).
  • Xapian adapter is mostly functional now.
  • Hyperestraier swig adapter is mostly functional now.
  • Removed capability bits. This was ugly and I think the API is more Pythonic without it.
  • Added Lupy adapter.
  • Added a ComponentFactory mechanism for passing URI query parameters to component constructors.
  • Added an update(document) method to Indexer. Default behaviour is to simply discard and re-index.
  • Solved the state storage issue by allowing the Indexer to store index state. If the Indexer is incapable of this, a StateStore object can be provided directly to the Framework.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • pyndexter/branches/refactoring/pyndexter/indexers/default.py

    r362 r364  
    9696    """ Default indexer, using bigrams. """ 
    9797 
    98     capabilities = CAP_READONLY | CAP_HITCOUNT | CAP_UNION | \ 
    99                    CAP_INTERSECTION | CAP_ITERATION | CAP_LIST | \ 
    100                    CAP_ATTRIBUTES | CAP_WHOLEWORD 
    101  
    10298    _tokeniser = re.compile(r'\w+') 
    10399 
  • pyndexter/branches/refactoring/pyndexter/indexers/hyperestraier.py

    r361 r364  
    77# 
    88 
     9""" 
     10Adapter for Hyperestraier using the swigged bindings 
     11(http://hyperestraier.sourceforge.net/) 
     12""" 
     13 
    914import os 
    1015import HyperEstraier 
     
    1217 
    1318 
    14 __all__ = ['create_indexer', 'HyperestraierIndexer', 'HyperestraierSearch'] 
    15  
    16  
    17 def indexer(framework, **args): 
    18     """ Create HyperEstraier Pyndexter adapter. """ 
    19     from pyndexter.util import cast_args 
    20     args = cast_args(args, {'hype_mode': int}) 
    21     return HyperEstraierIndexer(framework, **args) 
     19__all__ = ['HyperestraierIndexer', 'HyperestraierResult'] 
    2220 
    2321 
    2422class HyperestraierIndexer(Indexer): 
    2523    """ Pyndexter adapter for the Hyperestraier indexer. """ 
    26     capabilities = CAP_READONLY | CAP_CONTENT | CAP_ATTRIBUTES | CAP_ORDERING |\ 
    27                    CAP_HITCOUNT | CAP_LIST | CAP_RELEVANCE | CAP_WHOLEWORD | \ 
    28                    CAP_ASTERISK | CAP_INTERSECTION 
    29  
    30     def __init__(self, framework, hype_mode=None, **ignore): 
     24    def __init__(self, framework, path, hype_mode=None, **ignore): 
    3125        Indexer.__init__(self, framework) 
    3226        self.hype_mode = hype_mode 
    3327 
    34         self.path = os.path.join(framework.path, 'hyperestraier.db').encode('utf-8') 
     28        self.path = path 
     29        self.db_path = os.path.join(path, 'hyperestraier.db').encode('utf-8') 
     30        self.state_path = os.path.join(path, 'state.db') 
    3531 
    3632        if framework.mode == READWRITE: 
     
    6965            raise DocumentNotFound(uri) 
    7066        self.db.out_doc(id, HyperEstraier.Database.ODCLEAN) 
     67 
     68    def state_store(self): 
     69        return StateStore(self.state_path) 
    7170 
    7271    def search(self, query): 
     
    110109#        if order is not None: 
    111110#            search = search.order(order) 
    112         return HyperestraierSearch(self, phrase, search) 
     111        return HyperestraierResult(self, phrase, search) 
    113112 
    114113    # Internal methods 
     
    139138 
    140139 
    141 class HyperestraierSearch(Search): 
     140indexer_factory = ComponentFactory(HyperestraierIndexer, hype_mode=int) 
     141 
     142 
     143class HyperestraierResult(Result): 
    142144    def __iter__(self): 
    143145        for id in self.context: 
  • pyndexter/branches/refactoring/pyndexter/indexers/__init__.py

    r361 r364  
    77# 
    88 
    9 __all__ = ['hyperestraier', 'xapian', 'lucene', 'lupy', 'default', 'pyrex'] 
     9__all__ = ['hyperestraier', 'hype', 'xapian', 'lucene', 'lupy', 'default', 'pyrex'] 
  • pyndexter/branches/refactoring/pyndexter/indexers/lupy.py

    r362 r364  
     1# -*- coding: utf-8 -*- 
     2# 
     3# Copyright (C) 2006 Alec Thomas <alec@swapoff.org> 
     4# 
     5# This software is licensed as described in the file COPYING, which 
     6# you should have received as part of this distribution. 
     7# 
     8 
     9""" 
     10Adapter for the (deprecated, but still available from 
     11http://gentoo.prz.rzeszow.pl/distfiles/Lupy-0.2.1.tar.gz) Lupy indexer. 
     12""" 
     13 
    114import os 
    215from pyndexter import * 
    316lupy = __import__('lupy', {}, {}, ['']) 
    417lupy.indexer = __import__('lupy.indexer', {}, {}, ['']) 
     18lupy.search = __import__('lupy.search', {}, {}, ['']) 
    519 
    620class LupyIndexer(Indexer): 
    7     """ Adapter for the (deprecated, but still available from 
    8     http://gentoo.prz.rzeszow.pl/distfiles/Lupy-0.2.1.tar.gz) Lupy indexer. """ 
    9  
    10     def bind(self, framework): 
    11         self.path = os.path.join(framework.path, 'lupy.db').encode('utf-8') 
    12  
    13         self.db = lupy.indexer.Index(self.path, create=framework.mode == READWRITE and not os.path.exists(self.path)) 
     21    def __init__(self, framework, path): 
     22        Indexer.__init__(self, framework) 
     23        self.path = path 
     24        self.db_path = os.path.join(self.path, 'lupy.db').encode('utf-8') 
     25        self.state_path = os.path.join(self.path, 'state.db') 
     26        if framework.mode == READWRITE and not os.path.exists(self.path): 
     27            os.makedirs(self.path) 
     28        self.db = lupy.indexer.Index(self.db_path, 
     29                                     create=framework.mode == \ 
     30                                     READWRITE and not os.path.exists(self.db_path)) 
    1431 
    1532 
     
    2542 
    2643    def search(self, query): 
    27         ands, ors, nots = [], [], [] 
    28         self._compile_query(query, ands, ors, nots) 
     44        lupy_query = lupy.indexer.BooleanQuery() 
     45        self._compile_query(query, (True, False), lupy_query) 
     46        searcher = lupy.search.indexsearcher.IndexSearcher(self.db_path) 
     47        hits = searcher.search(lupy_query) 
     48        return LupyResult(self, query, hits) 
    2949 
    3050    def optimise(self): 
    3151        self.db.optimize() 
    3252 
     53    def close(self): 
     54        self.db.close() 
     55 
     56    def state_store(self): 
     57        return StateStore(self.state_path) 
     58 
    3359    # Internal methods 
    34     def _compile_query(self, node, ands, ors, nots): 
     60    def _compile_query(self, node, op, query): 
    3561        if not node or node.type == node.NULL: 
    36             return '' 
     62            return 
    3763        if node.type == node.AND: 
    38             self._compile_query(node.left) 
     64            self._compile_query(node.left, (True, False), query) 
     65            self._compile_query(node.right, (True, False), query) 
     66        elif node.type == node.OR: 
     67            self._compile_query(node.left, (False, False), query) 
     68            self._compile_query(node.right, (False, False), query) 
     69        elif node.type == node.NOT: 
     70            self._compile_query(node.left, (False, True), query) 
     71        elif node.type == node.TERM: 
     72            query.add(lupy.indexer.TermQuery(lupy.indexer.Term('text', node.value)), *op) 
     73        else: 
     74            raise NotImplementedError 
    3975 
    4076 
    41 class LupySearch(Search): 
     77indexer_factory = ComponentFactory(LupyIndexer) 
     78 
     79class LupyResult(Result): 
    4280    def __iter__(self): 
    43         pass 
     81        for hit in self.context: 
     82            fields = dict([(str(k), hit.get(k)) for k in hit.fieldNames]) 
     83            yield Hit(document=self.indexer.framework.fetch, 
     84                      **fields) 
     85 
     86 
  • pyndexter/branches/refactoring/pyndexter/indexers/xapian.py

    r363 r364  
    77# 
    88 
     9""" 
     10Adapter for Xapian (http://www.xapian.org) 
     11""" 
     12 
    913import os 
    1014import re 
     
    1216xapian = __import__('xapian') 
    1317 
    14 def create_indexer(framework, path=None, stemmer='english', words=r'\w+', 
    15                    **ignore): 
    16     return XapianIndexer(framework, path=path, stemmer=stemmer, words=words) 
     18 
     19__all__ = ['XapianIndexer', 'XapianResult'] 
     20 
    1721 
    1822class XapianIndexer(Indexer): 
     
    7074        enquire = xapian.Enquire(self.db) 
    7175        enquire.set_query(query) 
    72         return XapianSearch(self, query, enquire) 
     76        return XapianResult(self, query, enquire) 
    7377 
    7478    def state_store(self): 
     
    8084        if node.type == node.AND: 
    8185            return '%s AND %s' % (self._compile_query(node.left), 
    82                               self._compile_query(node.right)) 
     86                                  self._compile_query(node.right)) 
    8387        elif node.type == node.OR: 
    8488            return '%s OR %s' % (self._compile_query(node.left), 
     
    9296 
    9397 
    94 class XapianSearch(Search): 
     98indexer_factory = ComponentFactory(XapianIndexer) 
     99 
     100 
     101class XapianResult(Result): 
    95102    def __iter__(self): 
    96         matches = self.context.get_mset(0, 10) 
     103        matches = self.context.get_mset(0, 20) 
    97104        for hit in matches: 
    98105            doc = hit[xapian.MSET_DOCUMENT] 
  • pyndexter/branches/refactoring/pyndexter/__init__.py

    r362 r364  
    1111import pickle 
    1212import gzip 
     13import inspect 
    1314from StringIO import StringIO 
    1415from urlparse import urlsplit, urlunsplit 
     
    3233READONLY READWRITE 
    3334 
    34 CAP_READONLY CAP_ORDERING CAP_CONTENT CAP_ATTRIBUTES CAP_RELEVANCE CAP_HITCOUNT 
    35 CAP_LIST CAP_ITERATION CAP_ASTERISK CAP_QUESTION CAP_WHOLEWORD CAP_UNION 
    36 CAP_INTERSECTION 
    37  
    38 SEARCH_WHOLEWORD SEARCH_ASTERISK SEARCH_QUESTION SEARCH_UNION 
    39  
    40 Query Framework Document Source Indexer Search Hit 
     35Query Framework Document Source Indexer Result Hit StateStore ComponentFactory 
    4136""".split() 
    4237 
     
    5045READWRITE = 1 
    5146 
    52 # Indexer capabilities 
    53 CAP_READONLY = 1        # Supports read-only access to the index 
    54 CAP_ORDERING = 2        # Supports result ordering 
    55 CAP_CONTENT = 4         # Can fetch() document content 
    56 CAP_ATTRIBUTES = 8      # Supports per-document attributes 
    57 CAP_RELEVANCE = 16      # Can return results by relevance 
    58 CAP_HITCOUNT = 32       # Search result supports len() 
    59 CAP_LIST = 64           # Search result supports list-style lookup 
    60 CAP_ITERATION = 128     # Supports index iteration 
    61 CAP_ASTERISK = 256      # Supports the asterisk wildcard (*<term>*) 
    62 CAP_QUESTION = 512      # Supports the single character wildcard (a?c) 
    63 CAP_WHOLEWORD = 512     # Performs whole word searches by default 
    64 CAP_UNION = 1024        # Supports unions (ie. matches documents with any word) 
    65 CAP_INTERSECTION = 2048 # Supports intersections (ie. matches documents with 
    66                         # all words) 
    67  
    68 # Search flags. Flags may be ignored if the indexer does not support a 
    69 # particular feature. 
    70 SEARCH_WHOLEWORD = 1    # Perform a wholeword search 
    71 SEARCH_ASTERISK = 2     # Allow wildcard (*) in search term 
    72 SEARCH_QUESTION = 4     # Allow single character wildcard (?) in search term 
    73 SEARCH_UNION = 8        # Whether to perform a union rather than an 
    74                         # intersection (the default) of search term results 
    7547 
    7648class Error(Exception): 
     
    361333 
    362334 
     335class StateStore(object): 
     336    """A class providing file-like objects for storage and retrieval of 
     337    framework state.""" 
     338 
     339    def __init__(self, path): 
     340        self.path = path 
     341 
     342    def store(self): 
     343        """Return a file-like object for storing state.""" 
     344        return open(self.path, 'wb') 
     345 
     346    def retrieve(self): 
     347        """Return a file-like object for fetching state.""" 
     348        return open(self.path, 'rb') 
     349 
     350    def exists(self): 
     351        """Does the state store exist?""" 
     352        return os.path.exists(self.path) 
     353 
     354 
    363355class Indexer(object): 
    364     """ An Indexer performs document indexing and searching. This base object 
    365     provides a framework for indexers. """ 
    366  
    367     capabilities = 0 
     356    """An Indexer performs document indexing and searching. This base object 
     357    provides a framework for indexers.""" 
    368358 
    369359    def __init__(self, framework): 
     
    395385        raise NotImplementedError 
    396386 
     387    def update(self, document): 
     388        """Update a document in the index. Default is to `discard()` and 
     389        `index()`.""" 
     390        self.discard(document.uri) 
     391        self.index(document) 
     392 
    397393    def optimise(self): 
    398394        """ Optimise the indexer. """ 
     
    401397        """ Synchronise indexer with stored representation. """ 
    402398 
     399    def state_store(self): 
     400        """If this Indexer is capable of storing framework state, return a 
     401        `StateStore` object.""" 
     402        return None 
     403 
     404 
     405class ComponentFactory(object): 
     406    """Factory for translating URL-style query parameters into a standard 
     407    constructor call.""" 
     408 
     409    class List(object): 
     410        """Translate a parameter that is a list of elements of `type`.""" 
     411        def __init__(self, type): 
     412            self.type = type 
     413 
     414        def __call__(self, value): 
     415            return [self.type(v) for v in value] 
     416 
     417    def __init__(self, indexer, **arg_types): 
     418        """Create a new factory. 
     419 
     420        arg_types is a dictionary of <arg>:<type> mappings.""" 
     421 
     422        self.indexer = indexer 
     423        self.arg_types = arg_types 
     424        args, varargs, self.varkw, defaults = \ 
     425            inspect.getargspec(self.indexer.__init__) 
     426        defaults = defaults or [] 
     427        self.defaults = dict(zip(list(args[-len(defaults):]), defaults)) 
     428        self.defaults.pop('self', None) 
     429        self.args = defaults and args[:-len(defaults)] or args 
     430 
     431    def __call__(self, framework, **kwargs): 
     432        args = dict(self.defaults.items()) 
     433        args['framework'] = framework 
     434        args.update(kwargs) 
     435 
     436        # Translate all remaining arguments 
     437        for k, v in args.items(): 
     438            if v is not None and k in self.arg_types: 
     439                type = self.arg_types[k] 
     440                # If it's a list, and not marked as such, convert it to a scalar 
     441                if isinstance(v, (tuple, list)) and not isinstance(type, self.List): 
     442                    if len(v) != 1: 
     443                        raise ValueError('argument "%s" should be a scalar' % k) 
     444                    v = v[0] 
     445                args[k] = type(v) 
     446 
     447        return self.indexer(**args) 
     448 
    403449 
    404450class Framework(object): 
    405     """ The glue. Ties `Indexer` and `Source` together, performs housekeeping 
    406     tasks and provides a convenient interface to it all. """ 
    407     def __init__(self, path, indexer, sources=[], mode=READWRITE, 
    408                  indexer_args={}): 
    409         self.path = path 
    410         self.state_path = os.path.join(self.path, 'state.db') 
     451    """The glue. Ties `Indexer` and `Source` together, performs housekeeping 
     452    tasks and provides a convenient interface to it all. 
     453 
     454    If the `Indexer` is not capable of storing state and automatic updates are 
     455    desired, a `StateStore` object should be passed to the `Framework`.""" 
     456 
     457    def __init__(self, indexer, sources=[], mode=READWRITE, 
     458                 indexer_args={}, state_store=None): 
    411459        self.mode = mode 
    412460 
    413         if not os.path.exists(self.path): 
    414             os.makedirs(self.path) 
    415  
    416461        self.indexer = self._load_plugin('indexer', indexer, indexer_args) 
    417462 
     463        if state_store is None: 
     464            self.state_store = self.indexer.state_store() 
     465        else: 
     466            self.state_store = state_store 
     467 
    418468        sources = [self._load_plugin('source', source) for source in sources] 
     469 
    419470        from pyndexter.sources.metasource import MetaSource 
    420471        self.source = MetaSource(self) 
     
    423474 
    424475    def add_source(self, source, source_args={}): 
    425         """ Add a source to be indexed to the framework. """ 
     476        """ Add a source to be indexed to the framework. Can either be a 
     477        `Source` instance or a URI.""" 
    426478        if isinstance(source, basestring): 
    427479            source = self._load_plugin('source', source, source_args) 
     
    430482    def fetch(self, uri): 
    431483        """ Fetch a document. """ 
    432         if not self.source: 
    433             raise SourceError("Can't fetch documents without a document source") 
    434484        return self.source.fetch(uri) 
    435485 
    436486    def __iter__(self): 
    437487        """ Iterate over all URI's in the document source. """ 
    438         if not self.source: 
    439             raise SourceError("Can't iterate over URI's without a document source") 
    440488        for uri in self.source: 
    441489            yield uri 
     
    444492        """ Update the index with the current state of the document source. """ 
    445493        self._assert_rw() 
    446         if not self.source: 
    447             raise IndexerError("Can't perform automatic update without a Source.") 
    448         if not self.state_path: 
    449             raise IndexerError("Source state path not set, Indexer is not " 
    450                                "capable of automatic updates.") 
    451         if os.path.exists(self.state_path): 
    452             state = open(self.state_path) 
    453             for transition, uri in self.source.difference(state): 
     494        if not self.state_store: 
     495            raise IndexerError("Source state storage path not defined, " 
     496                               "Framework is not capable of automatic updates.") 
     497        if self.state_store.exists(): 
     498            store = self.state_store.retrieve() 
     499            for transition, uri in self.source.difference(store): 
    454500                if transition == REMOVED: 
    455501                    self.discard(uri) 
     
    501547        """ Synchronise indexer with on-disk representation. """ 
    502548        if self.mode == READWRITE: 
    503             self._sync_source_state() 
     549            if self.mode == READWRITE and self.state_store: 
     550                store = self.state_store.store() 
     551                self.source.marshal(store) 
    504552            self.indexer.sync() 
    505553 
     
    507555    def _load_plugin(self, type, uri, args={}): 
    508556        from pyndexter.util import uri_parse 
    509         scheme, username, password, netloc, path, parameters, query, fragment = \ 
     557        # Extract URI components 
     558        scheme, username, password, netloc, path, query, fragment = \ 
    510559            uri_parse(uri) 
    511         query.update(indexer_args) 
    512         module = __import__('pyndexter.%ss.%s' % (type, scheme), {}, {}, ['']) 
    513         return getattr(module, type)(self, username=username, password=password, 
    514                                      netloc=netloc, path=path, 
    515                                      parameters=parameters, fragment=fragment, 
    516                                      **query) 
     560        username = username or None 
     561        password = password or None 
     562        uri_components = {'username': username, 'password': password, 
     563                          'netloc': netloc, 'path': path, 'fragment': fragment} 
     564        # Discard them if they're empty 
     565        uri_components = dict([(k, v) for k, v in uri_components.iteritems() if v]) 
     566        query.update(uri_components) 
     567        query.update(args) 
     568        module = __import__('pyndexter.%ss.%s' % (type, scheme), 
     569                            {}, {}, ['']) 
     570        indexer_factory = getattr(module, type + '_factory') 
     571        assert isinstance(indexer_factory, ComponentFactory) 
     572        return indexer_factory(self, **query) 
    517573 
    518574    def _assert_rw(self): 
     
    521577                              "operation" % self.__class__.__name__) 
    522578 
    523     def _sync_source_state(self): 
    524         """ Save Source objects state to the location defined in the 
    525         constructor. """ 
    526         if self.mode == READWRITE and self.source and self.state_path: 
    527             file = open(self.state_path, 'wb') 
    528             self.source.marshal(file) 
    529             file.close() 
    530  
    531  
    532 class Search(object): 
     579 
     580class Result(object): 
    533581    """ Represents the result of a search. Each hit is returned as a Hit 
    534582    object. """ 
  • pyndexter/branches/refactoring/pyndexter/sources/file.py

    r362 r364  
    1212from stat import * 
    1313from urlparse import urlsplit, urlunsplit 
     14from pyndexter import * 
    1415 
    15 from pyndexter import Source, Document, DocumentNotFound 
    1616 
    1717class FileSource(Source): 
    18     def __init__(self, framework, root, include=None, exclude=None, predicate=None): 
     18    def __init__(self, framework, path, include=None, exclude=None, predicate=None): 
    1919        """ Expose a subset of the file system for searching. """ 
    2020        Source.__init__(self, framework, include, exclude, predicate) 
    21         self.root = os.path.normpath(root
     21        self.path = os.path.normpath(path
    2222        self.encoding = sys.getfilesystemencoding() 
    2323 
     
    2525        def walk_path(path): 
    2626            path = path.strip(os.path.sep) 
    27             root_path = os.path.join(self.root, path) 
     27            root_path = os.path.join(self.path, path) 
    2828            for file in os.listdir(root_path): 
    2929                full_path = os.path.join(root_path, file) 
     
    4747        path = os.path.normpath(path) 
    4848        return scheme == 'file' and \ 
    49                path.startswith(self.root) and \ 
     49               path.startswith(self.path) and \ 
    5050               self.predicate(path) 
    5151 
     
    6565 
    6666    def __hash__(self): 
    67         return hash(self._file2uri(self.root) + '-'.join(self.exclude) + \ 
     67        return hash(self._file2uri(self.path) + '-'.join(self.exclude) + \ 
    6868                    '+'.join(self.include)) 
    6969 
     
    7878    def _uri2file(self, uri): 
    7979        scheme, location, path, query, fragment = urlsplit(uri, 'file') 
    80         if scheme not in 'file': 
     80        if scheme != 'file': 
    8181            raise InvalidURI("URI scheme in '%s' not supported by FileSource" 
    8282                             % scheme) 
    8383        path = os.path.normpath(path) 
    84         if not path.startswith(self.root): 
     84        if not path.startswith(self.path): 
    8585            raise InvalidURI("Requested URI '%s' is not from this FileSource" 
    8686                             % uri) 
    8787        return path.decode(self.encoding) 
     88 
     89 
     90source_factory = ComponentFactory(FileSource, 
     91                                  include=ComponentFactory.List(str), 
     92                                  exclude=ComponentFactory.List(str)) 
  • pyndexter/branches/refactoring/pyndexter/sources/metasource.py

    r361 r364  
    77# 
    88 
     9import pickle 
     10from StringIO import StringIO 
    911from pyndexter import * 
    1012from urlparse import urlsplit 
    11 import pickle 
    1213 
    1314class MetaSource(Source): 
     
    5657        file.write(pickle.dumps(state, 2)) 
    5758 
    58     def difference(self, state): 
     59    def difference(self, file): 
    5960        try: 
    60             state = pickle.loads(state
     61            state = pickle.loads(file.read()
    6162        except Exception, e: 
    6263            raise InvalidState('Invalid state provided to MetaSource. ' 
     
    6768                    yield (ADDED, uri) 
    6869            else: 
    69                 for change in source.difference(state[hash(source)]): 
     70                pseudo_file = StringIO(state[hash(source)]) 
     71                for change in source.difference(pseudo_file): 
    7072                    yield change 
  • pyndexter/branches/refactoring/pyndexter/util.py

    r361 r364  
    1313except: 
    1414    from sets import Set as set 
     15 
    1516 
    1617class CacheDict(DictMixin): 
     
    6061    `cgi.parse_qs()`. 
    6162 
    62     scheme://username:password@netloc/path;parameters?query#fragment 
     63    scheme://username:password@netloc/path?query#fragment 
     64 
     65    TODO: Support "parameters???" Never seen this: 
     66        scheme://username:password@netloc/path;parameters?query#fragment 
    6367 
    6468    PS. `urlparse` is not useful. """ 
     
    7579    groups = match.groups() 
    7680    return groups[0:5] + (parse_qs(groups[5] or ''),) + groups[6:] 
    77  
    78  
    79 def cast_args(args, types): 
    80     """ Cast a set of arguments to the types represented in types. types is a 
    81     dictionary of argument names and their associated type. """ 
    82     cast = {} 
    83     nop = lambda o: o 
    84     for k, v in args.iteritems(): 
    85         if k in strip: 
    86             continue 
    87         if v is not None: 
    88             v = types.get(k, nop)(v) 
    89         cast[k] = v 
    90     return cast 
  • pyndexter/branches/refactoring/.todo

    r357 r364  
    44    </title> 
    55    <note priority="medium" time="1145722536"> 
    6         Callbacks for index() and discard(), perhaps something similar for Source objects 
     6        Callbacks for index() and discard(), perhaps something similar for Source objects? 
    77    </note> 
    88    <note priority="medium" time="1145802778"> 
     
    3030        Refactor Indexer into two classes: the Indexer itself, and a class that glues Source and the Indexer together. This would remove the duplication I'm getting in all the stock methods (update, index, fetch, etc.) 
    3131    </note> 
     32    <note priority="medium" time="1168868728"> 
     33        Add slicing on Search objects. This will allow fast pagination in result displays. 
     34    </note> 
     35    <note priority="low" time="1168875038"> 
     36        Add some "stock" query translators (eg. a AND b OR c style, a b or c, +a +b c, etc.) 
     37    </note> 
    3238</todo>