Source code for bibolamazi.core.bibfilter

# -*- coding: utf-8 -*-
################################################################################
#                                                                              #
#   This file is part of the Bibolamazi Project.                               #
#   Copyright (C) 2014 by Philippe Faist                                       #
#   philippe.faist@bluewin.ch                                                  #
#                                                                              #
#   Bibolamazi is free software: you can redistribute it and/or modify         #
#   it under the terms of the GNU General Public License as published by       #
#   the Free Software Foundation, either version 3 of the License, or          #
#   (at your option) any later version.                                        #
#                                                                              #
#   Bibolamazi is distributed in the hope that it will be useful,              #
#   but WITHOUT ANY WARRANTY; without even the implied warranty of             #
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              #
#   GNU General Public License for more details.                               #
#                                                                              #
#   You should have received a copy of the GNU General Public License          #
#   along with Bibolamazi.  If not, see <http://www.gnu.org/licenses/>.        #
#                                                                              #
################################################################################


import bibolamazi.init
from bibolamazi.core.butils import BibolamaziError


[docs]class BibFilterError(BibolamaziError): """ Exception a filter should raise if it encounters an error. """ def __init__(self, filtername, message): if not isinstance(filtername, str): filtername = '<unknown>' super().__init__("Filter ‘{}’: {}".format(filtername, str(message))) self.filtername = filtername self.message = message
[docs]class BibFilter: """ Base class for a `bibolamazi` filter. To write new filters, you should subclass `BibFilter` and reimplement the relevant methods. See documentation of the different methods below to understand which to reimplement. """ # # constants # BIB_FILTER_SINGLE_ENTRY = 1 """ A constant that indicates that the filter should act upon individual entries only. See documentation for the :py:meth:`action()` method for more details. """ BIB_FILTER_BIBOLAMAZIFILE = 3 """ A constant that indicates that the filter should act upon the whole bibliography at once. See documentation for the :py:meth:`action()` method for more details. """ # # to be overridden by subclasses: # helpauthor = ""; # semicolon for docstring """ Your subclass should provide a `helpauthor` attribute, containing a one-line notice with the name of the author that wrote the filter code. You may also add a copyright notice. The exact format is not specified. This text is typically displayed at the top of the page generated by ``bibolamazi --help <filter>``. You should also avoid accessing this class attribute, you should use :py:meth:`getHelpAuthor()` instead, which will ensure that whitespace is properly stripped. """ helpdescription = "Some filter that filters some entries"; # semicolon for docstring """ Your subclass should provide a `helpdescription` attribute, containing a one-line description of what your filter does. This is typically displayed when invoking ``bibolamazi --list-filters``, along with the filter name. You should also avoid accessing this class attribute, you should use :py:meth:`getHelpDescription()` instead, which will ensure that whitespace is properly stripped. """ helptext = ""; # semicolon for docstring """ Your subclass should provide a `helptext` attribute, containing a possibly long, as detailed as possible description of how to use your filter. You don't need to provide the basic 'usage' and option list, which are automatically generated; but you should include all the text that would appear after the option list. This is typically displayed when invoking ``bibolamazi --help <filter>``. You should also avoid accessing this class attribute, you should use :py:meth:`getHelpText()` instead, which will ensure that whitespace is properly stripped. """ def __init__(self, **kwargs): """ Constructor. Sets the filtername to the name of the filter class. A warning is emitted for each argument in `kwargs`, which presumably was ignored by the superclass and passed on. """ super().__init__() self._bibolamazifile = None self._filtername = self.__class__.__name__ for k,v in kwargs.items(): logger.warning("Warning: %s: discarding unused argument: %s=%r", self._filtername, k, v)
[docs] def action(self): """ Return one of :py:const:`~BibFilter.BIB_FILTER_SINGLE_ENTRY` or :py:const:`~BibFilter.BIB_FILTER_BIBOLAMAZIFILE`, which tells how this filter should function. Depending on the return value of this function, either :py:meth:`filter_bibentry()` or :py:meth:`filter_bibolamazifile()` will be called. If the filter wishes to act on individual entries (like the built-in `arxiv` or `url` filters), then the subclass should return :py:const:`BibFilter.BIB_FILTER_SINGLE_ENTRY`. At the time of filtering the data, the function :py:meth:`filter_bibentry()` will be called repeatedly for each entry of the database. If the filter wishes to act on the full database at once (like the built-in `duplicates` filter), then the subclass should return :py:const:`~BibFilter.BIB_FILTER_BIBOLAMAZIFILE`. At the time of filtering the data, the function :py:meth:`filter_bibolamazifile()` will be called once with the full :py:class:`~core.bibolamazifile.BibolamaziFile` object as parameter. Note this is the only way to add or remove entries to or from the database, or to change their order. Note that when the filter is instantiated by a :py:class:`~core.bibolamazifile.BibolamaziFile` (as is most of the time in practice), then the function :py:meth:`bibolamaziFile()` will always return a valid object, independently of the filter's way of acting. """ raise BibFilterError(self.name(), 'BibFilter subclasses must reimplement action()!')
[docs] def prerun(self, bibolamazifile): """ This function gets called immediately before the filter is run, after any preceeding filters have been executed. It is not very useful if the :py:meth:`action()` is :py:const:`BibFilter.BIB_FILTER_BIBOLAMAZIFILE`, but it can prove useful for filters with action :py:const:`BibFilter.BIB_FILTER_SINGLE_ENTRY`, if any sort of pre-processing task should be done just before the actual filtering of the data. The default implementation does nothing. """ return
[docs] def postrun(self, bibolamazifile): """ This function gets called immediately after the filter is run, before any further filters are executed. It is not very useful if the :py:meth:`action()` is :py:const:`BibFilter.BIB_FILTER_BIBOLAMAZIFILE`, but it can prove useful for filters with action :py:const:`BibFilter.BIB_FILTER_SINGLE_ENTRY`, if any sort of global post-processing task should be done immediately after the actual filtering of the data. You can use this function, e.g., to produce an aggregated warning or report message. This method is not called if the filter raised an exception, whether internal or not. The default implementation does nothing. """ return
[docs] def filter_bibentry(self, x): """ The main filter function for filters that filter the data entry by entry. If the subclass' :py:meth:`action()` function returns :py:const:`BibFilter.BIB_FILTER_SINGLE_ENTRY`, then the subclass must reimplement this function. Otherwise, this function is never called. The object `x` is a :py:class:`pybtex.database.Entry` object instance, which should be updated according to the filter's action and purpose. The return value of this function is ignored. Subclasses should report warnings and logging through Python's logging mechanism (see doc of :py:mod:`core.blogger`) and should raise errors as :py:class:`BibFilterError` (preferrably, a subclass). Other raised exceptions will be interpreted as internal errors and will open a debugger. """ raise BibFilterError(self.name(), 'filter_bibentry() not implemented !')
[docs] def filter_bibolamazifile(self, x): """ The main filter function for filters that filter the data entry by entry. If the subclass' :py:meth:`action()` function returns :py:const:`BibFilter.BIB_FILTER_SINGLE_ENTRY`, then the subclass must reimplement this function. Otherwise, this function is never called. The object `x` is a :py:class:`~core.bibolamazifile.BibolamaziFile` object instance, which should be updated according to the filter's action and purpose. The return value of this function is ignored. Subclasses should report warnings and logging through Python's logging mechanism (see doc of :py:mod:`core.blogger`) and should raise errors as :py:class:`BibFilterError` (preferrably, a subclass). Other raised exceptions will be interpreted as internal errors and will open a debugger. """ raise BibFilterError(self.name(), 'filter_bibolamazifile() not implemented !')
[docs] def requested_cache_accessors(self): """ This function should return a list of :py:class:`bibusercache.BibUserCacheAccessor` class names of cache objects it would like to use. The relevant caches are then collected from the various filters and automatically instantiated and initialized. The default implementation of this function returns an empty list. Subclasses should override if they want to access the bibolamazi cache. """ return []
# --------------------------------------------------------------------------
[docs] def name(self): """ Returns the name of the filter as it was invoked in the bibolamazifile. This might be with, or without, the filterpackage. This information should be only used for reporting purposes and might slightly vary. If the filter was instantiated manually, and :py:meth:`setInvokationName()` was not called, then this function returns the class name. The subclass should not reimplement this function unless it really really really *really* feels it needs to. """ return self._filtername
[docs] def setInvokationName(self, filtername): """ Called internally by bibolamazifile, so that :py:meth:`name()` returns the name by which this filter was invoked. This function sets exactly what :py:meth:`name()` will return. Subclasses should not reimplement, the default implementation should suffice. """ self._filtername = filtername
[docs] def setBibolamaziFile(self, bibolamazifile): """ Remembers `bibolamazifile` as the :py:class:`~core.bibolamazifile.BibolamaziFile` object that we will be acting on. There's no use overriding this. When writing filters, there's also no need calling this explicitly, it's done in :py:class:`~core.bibolamazifile.BibolamaziFile`. """ self._bibolamazifile = bibolamazifile
[docs] def bibolamaziFile(self): """ Get the :py:class:`~core.bibolamazifile.BibolamaziFile` object that we are acting on. (The one previously set by :py:meth:`setBibolamaziFile()`.) There's no use overriding this. """ return self._bibolamazifile
[docs] def cacheAccessor(self, klass): """ A shorthand for calling the :py:meth:`cacheAccessor()` method of the bibolamazi file returned by :py:meth:`bibolamaziFile()`. """ return self.bibolamaziFile().cacheAccessor(klass)
[docs] def getRunningMessage(self): """ Return a nice message to display when invoking the fitler. The default implementation returns :py:meth:`name()`. Define this to whatever you want in your subclass to describe what you're doing. The core bibolamazi program displays this information to the user as it runs the filter. """ return self.name()
# convenience functions, no need to (i.e. should not) override
[docs] @classmethod def getHelpAuthor(cls): """ Convenience function that returns :py:data:`helpauthor`, with whitespace stripped. Use this when getting the contents of the helpauthor text. There's no need to (translate: you should not) reimplement this function in your subclass. """ return cls.helpauthor.strip()
[docs] @classmethod def getHelpDescription(cls): """ Convenience function that returns :py:data:`helpdescription`, with whitespace stripped. Use this when getting the contents of the helpdescription text. There's no need to (translate: you should not) reimplement this function in your subclass. """ return cls.helpdescription.strip()
[docs] @classmethod def getHelpText(cls): """ Convenience function that returns :py:data:`helptext`, with whitespace stripped. Use this when getting the contents of the helptext text. There's no need to (translate: you should not) reimplement this function in your subclass. """ return cls.helptext.strip()