text: import os
  import time
  import traceback
  
  from . import bibformat, cache
  from .settings import get_setting
  from ..external.frozendict import frozendict
  from .six import long
  from .system import make_dirs
  
  _VERSION = 2
  
  
  class BibCache(cache.InstanceTrackingCache, cache.GlobalCache):
      '''
      implements a cache for a bibliography file
  
      note that this differs somewhat from the other cache objects because it
      does not store multiple values, but only values derived from a single
      bib file
  
      note that the bibliography entries themselves are NOT stored in the
      in-memory cache which ONLY stores the formatted entries; instead,
      they are read from disk as necessary
      '''
  
      def __init__(self, bib_plugin_name, bib_file):
          self._inst_name = (bib_plugin_name, bib_file)
          super(BibCache, self).__init__()
  
          file_hash = cache.hash_digest(bib_file)
          self.bib_file = bib_file
          self.cache_name = "bib_{0}_{1}".format(bib_plugin_name, file_hash)
          self.formatted_cache_name = "bib_{0}_fmt_{1}".format(
              bib_plugin_name, file_hash
          )
  
      def get(self):
          try:
              result = self._objects[self.formatted_cache_name]
          except KeyError:
              try:
                  result = self.load(self.formatted_cache_name)
              except cache.CacheMiss:
                  result = None
  
          try:
              return self.validate_on_get(result)
          except cache.CacheMiss:
              return self._get_bib_cache()[1]
  
      def set(self, bib_entries):
          def _write_bib_cache():
              try:
                  cache.pickle.dumps(bib_entries, protocol=-1)
              except cache.pickle.PicklingError:
                  print('bib_entries must be pickleable')
                  traceback.print_exc()
              else:
                  with self._disk_lock:
                      make_dirs(self.cache_path)
                      self._write(
                          self.cache_name,
                          {self.cache_name: bib_entries}
                      )
  
          # write bib_entries to disk
          self._pool.apply_async(_write_bib_cache)
  
          formatted_entries = self._create_formatted_entries(bib_entries)
  
          with self._write_lock:
              self._objects[self.formatted_cache_name] = formatted_entries
              self._dirty = True
          self._schedule_save()
  
      def cache(self, func):
          try:
              return self.get()
          except:
              result = func()
              self.set(result)
              return result
  
      def validate_on_get(self, obj):
          if obj is None:
              raise cache.CacheMiss()
  
          meta_data, formatted_entries = obj
  
          try:
              mtime = os.path.getmtime(self.bib_file)
          except:
              raise cache.CacheMiss()
          else:
              if mtime > meta_data['cache_time']:
                  raise cache.CacheMiss('outdated formatted entries')
  
          if _VERSION != meta_data['version'] or any(
              meta_data[s] != get_setting("cite_" + s)
              for s in ["panel_format", "autocomplete_format"]
          ):
              return self._get_bib_cache()[1]
  
          return formatted_entries
  
      def _get_inst_key(self, *args, **kwargs):
          if not hasattr(self, '_inst_name'):
              if len(args) > 1:
                  return (args[0], args[1])
              else:
                  return None
          else:
              return self._inst_name
  
      def _get_bib_cache(self):
          try:
              cache_mtime = os.path.getmtime(
                  os.path.join(self.cache_path, self.cache_name))
  
              bib_mtime = os.path.getmtime(self.bib_file)
          except:
              raise cache.CacheMiss()
          else:
              if cache_mtime < bib_mtime:
                  raise cache.CacheMiss('outdated bib entry cache')
  
          bib_entries = self._read(self.cache_name)
          formatted_entries = self._create_formatted_entries(bib_entries)
          with self._write_lock:
              self._objects[self.formatted_cache_name] = formatted_entries
              self._dirty = True
          self._schedule_save()
  
          return formatted_entries
  
      def _create_formatted_entries(self, bib_entries):
          # create the formatted entries
          autocomplete_format = get_setting("cite_autocomplete_format")
          panel_format = get_setting("cite_panel_format")
  
          meta_data = frozendict(
              cache_time=long(time.time()),
              version=_VERSION,
              autocomplete_format=autocomplete_format,
              panel_format=panel_format
          )
  
          formatted_entries = tuple(
              frozendict(**{
                  "keyword": entry["keyword"],
                  "<prefix_match>": bibformat.create_prefix_match_str(entry),
                  "<panel_formatted>": tuple(
                      bibformat.format_entry(s, entry) for s in panel_format
                  ),
                  "<autocomplete_formatted>":
                      bibformat.format_entry(autocomplete_format, entry)
              })
              for entry in bib_entries
          )
  
          return meta_data, formatted_entries
  