Coverage for pydblite/pydblite : 71%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# # BSD licence # # Author : Pierre Quentel (pierre.quentel@gmail.com) #
except: import pickle
'LIKE': like, 'GLOB': operator.contains, "IN": _in, '=': operator.eq, '!=': operator.ne, '<': operator.lt, '<=': operator.le, '>': operator.gt, '>=': operator.ge}
return "" else: # Parent of two expressions else:
return self.expression_group.apply_filter(records)
"""Class used for indexing a base on a field. The instance of Index is an attribute of the Base instance"""
return iter(self.db.indices[self.field])
return self.db.indices[self.field].keys()
"""Lookup by key : return the list of records where field value is equal to this key, or an empty list""" ids = self.db.indices[self.field].get(key, []) return [self.db.records[_id] for _id in ids]
sqlite_compat=False): """protocol as defined in pickle / pickle. Defaults to the highest protocol available. For maximum compatibility use protocol = 0
""" """The path of the database in the file system""" """The basename of the path, stripped of its extension""" """The list of the fields (does not include the internal fields __id__ and __version__)""" # if base exists, get field names _in = open(self.path) # don't specify binary mode ! else:
""" Returns: - bool: if the database file exists """
""" Create a new base with specified field names.
Args: - \*fields (str): The field names to create. - mode (str): the mode used when creating the database.
- if mode = 'open' : open the existing base, ignore the fields - if mode = 'override' : erase the existing base and create a new one with the specified fields
Returns: - Returns the database (self). """ else:
self.fields.append(field["name"]) self.default_values[field["name"]] = field.get("default", None) self.fields.append(field[0]) self.default_values[field[0]] = field[1] else:
""" Create an index on the specified field names
An index on a field is a mapping between the values taken by the field and the sorted list of the ids of the records whose field is equal to this value
For each indexed field, an attribute of self is created, an instance of the class Index (see above). Its name it the field name, with the prefix _ to avoid name conflicts
Args: - fields (list): the fields to index """ raise NameError("%s is not a field name %s" % (f, self.fields)) # initialize the indices continue # use bisect to quickly insert the id in the list bisect.insort(self.indices[f].setdefault(record[f], []), _id) # create a new attribute of self, used to find the records # by this index
"""Delete the index on the specified fields""" raise ValueError("No index on field %s" % f)
"""Open an existing database and load its content into memory""" # guess protocol _in = open(self.path) # don't specify binary mode ! else: # If loading an old database, the default values do not exist except EOFError: self.default_values = {} setattr(self, '_' + f, Index(self, f))
"""Write the database to a file"""
""" Insert one or more records in the database.
Parameters can be positional or keyword arguments. If positional they must be in the same order as in the create() method If some of the fields are missing the value is set to None
Args: - args (values, or a list/tuple of values): The record(s) to insert. - kw (dict): The field/values to insert
Returns: - Returns the record identifier if inserting one item, else None. """ raise RuntimeError("Database columns have not been setup!") else: # initialize all fields to the default values # raise exception if unknown field raise NameError("Invalid field name : %s" % key) # set keys and values # add the key __id__ : record identifier # add the key __version__ : version number # create an entry in the dictionary self.records, indexed by __id__ # update index bisect.insort(self.indices[ix].setdefault(record[ix], []), self.next_id) # increment the next __id__
""" Remove a single record, or the records in an iterable
Before starting deletion, test if all records are in the base and don't have twice the same __id__
Args: - remove (record or list of records): The record(s) to delete.
Returns: - Return the number of deleted items """ else: # convert iterable into a list (to be able to sort it) remove = [r for r in remove] return 0 # check if the records are in the base missing = list(set(_ids).difference(keys)) raise IndexError('Delete aborted. Records with these ids' ' not found in the base : %s' % str(missing)) # raise exception if duplicate ids if _ids[i] == _ids[i + 1]: raise IndexError("Delete aborted. Duplicate id : %s" % _ids[i]) # remove id from indices pos = bisect.bisect(self.indices[indx][r[indx]], _id) - 1 del self.indices[indx][r[indx]][pos] if not self.indices[indx][r[indx]]: del self.indices[indx][r[indx]] # remove record from self.records
""" Update one record or a list of records with new keys and values and update indices
Args: - records (record or list of records): The record(s) to update. """ # ignore unknown fields # update indices for record in records: if record[indx] == kw[indx]: continue _id = record["__id__"] # remove id for the old value old_pos = bisect.bisect(self.indices[indx][record[indx]], _id) - 1 del self.indices[indx][record[indx]][old_pos] if not self.indices[indx][record[indx]]: del self.indices[indx][record[indx]] # insert new value bisect.insort(self.indices[indx].setdefault(kw[indx], []), _id) # update record values # increment version number
"""Adds a field to the database""" raise ValueError("Field %s already defined" % field) self.open()
"""Removes a field from the database""" if field in ["__id__", "__version__"]: raise ValueError("Can't delete field %s" % field) self.fields.remove(field) for r in self: del r[field] if field in self.indices: del self.indices[field] self.commit()
"""Selection by field values
db(key=value) returns the list of records where r[key] = value
Args: - args (list): A field to filter on. - kw (dict): pairs of field and value to filter on.
Returns: - When args supplied, return a Filter object that filters on the specified field. - When kw supplied, return all the records where field values matches the key/values in kw. """ raise SyntaxError("Can't specify positional AND keyword arguments")
raise SyntaxError("Only one field can be specified") return args[0].apply_filter(self.records) raise ValueError("%s is not a field" % args[0]) else:
# indices and non-indices # fast selection on indices ix = ixs.pop() res = set(self.indices[ix].get(kw[ix], [])) if not res: return [] while ixs: ix = ixs.pop() res = res & set(self.indices[ix].get(kw[ix], [])) else: # if no index, initialize result with test on first field # selection on non-index fields res = res & set([_id for _id in res if self.records[_id][field] == kw[field]])
# direct access by record id
raise ValueError("Filter argument is not of type " "'PyDbExpressionGroup': %s" % type(db_filter))
"""Delete by record id"""
return record_id in self.records
"""Returns the records grouped by column""" gropus = [(k, len(list(g))) for k, g in groupby(torrents_filter, key=lambda x: x[column])] result = {} for column, count in gropus: result[column] = result.get(column, 0) + count return [(c, result[c]) for c in result]
key=lambda x: x[group_by_field])]
"""Returns a set of unique values from column""" records = self(db_filter) else:
"""Returns the indices"""
"""Iteration on the records"""
"""Iteration on the records""" return iter(self.records.values())
else: Base = _BasePy3 |