class_docstr: Metaclass providing an implementation of the flyweight pattern.
  
  Since the flyweight is very similar to a singleton, we occasionally use this
  term to make clear there's only one instance (at a time).
  This integrates the "factory" into the actual classes, which need
  to have a class attribute `_unique_instances` (WeakValueDictionary).
  By providing an implementation of __call__, you don't need to call a
  factory's get_xy_repo() method to get a singleton. Instead this is called
  when you simply instantiate via MyClass(). So, you basically don't even need
  to know there were singletons. Therefore it is also less likely to sabotage
  the concept by not being aware of how to get an appropriate object.
  
  Multiple instances, pointing to the same physical repository can cause a
  lot of trouble. This is why this class exists. You should be very aware of
  the implications, if you want to circumvent that mechanism.
  
  To use this pattern, you need to add this class as a metaclass to the class
  you want to use it with. Additionally there needs to be a class attribute
  `_unique_instances`, which should be a `WeakValueDictionary`. Furthermore
  implement `_flyweight_id_from_args` method to determine, what should be the
  identifying criteria to consider two requested instances the same.
  
  Example:
  
  from weakref import WeakValueDictionary
  
  class MyFlyweightClass(object, metaclass=Flyweight):
  
      _unique_instances = WeakValueDictionary()
  
      @classmethod
      def _flyweight_id_from_args(cls, *args, **kwargs):
  
          id = kwargs.pop('id')
          return id, args, kwargs
  
      def __init__(self, some, someother=None):
          pass
  
  a = MyFlyweightClass('bla', id=1)
  b = MyFlyweightClass('blubb', id=1)
  assert a is b
  c = MyFlyweightClass('whatever', id=2)
  assert c is not a
class_docstr_tok: ["Metaclass","providing","an","implementation","of","the","flyweight","pattern",".","Since","the","flyweight","is","very","similar","to","a","singleton",",","we","occasionally","use","this","term","to","make","clear","there","'s","only","one","instance","(","at","a","time",")",".","This","integrates","the","``","factory","''","into","the","actual","classes",",","which","need","to","have","a","class","attribute","`","_unique_instances","`","(","WeakValueDictionary",")",".","By","providing","an","implementation","of","__call__",",","you","do","n't","need","to","call","a","factory","'s","get_xy_repo","(",")","method","to","get","a","singleton",".","Instead","this","is","called","when","you","simply","instantiate","via","MyClass","(",")",".","So",",","you","basically","do","n't","even","need","to","know","there","were","singletons",".","Therefore","it","is","also","less","likely","to","sabotage","the","concept","by","not","being","aware","of","how","to","get","an","appropriate","object",".","Multiple","instances",",","pointing","to","the","same","physical","repository","can","cause","a","lot","of","trouble",".","This","is","why","this","class","exists",".","You","should","be","very","aware","of","the","implications",",","if","you","want","to","circumvent","that","mechanism",".","To","use","this","pattern",",","you","need","to","add","this","class","as","a","metaclass","to","the","class","you","want","to","use","it","with",".","Additionally","there","needs","to","be","a","class","attribute","`","_unique_instances","`",",","which","should","be","a","`","WeakValueDictionary","`",".","Furthermore","implement","`","_flyweight_id_from_args","`","method","to","determine",",","what","should","be","the","identifying","criteria","to","consider","two","requested","instances","the","same",".","Example",":","from","weakref","import","WeakValueDictionary","class","MyFlyweightClass","(","object",",","metaclass=Flyweight",")",":","_unique_instances","=","WeakValueDictionary","(",")","@","classmethod","def","_flyweight_id_from_args","(","cls",",","*","args",",","*","*","kwargs",")",":","id","=","kwargs.pop","(","'id","'",")","return","id",",","args",",","kwargs","def","__init__","(","self",",","some",",","someother=None",")",":","pass","a","=","MyFlyweightClass","(","'bla","'",",","id=1",")","b","=","MyFlyweightClass","(","'blubb","'",",","id=1",")","assert","a","is","b","c","=","MyFlyweightClass","(","'whatever","'",",","id=2",")","assert","c","is","not","a"]
code: def __call__(cls, *args, **kwargs):
      id_, new_args, new_kwargs = cls._flyweight_id_from_args(
          *args, **kwargs
      )
      # Thread lock following block so we do not fall victim to
      # race condition across threads trying to instantiate multiple
      # instances. In principle we better have a lock per id_ but that mean we
      # might race at getting "name specific lock" (Yarik did not research much),
      # so keeping it KISS -- just lock instantiation altogether, but could be
      # made smarter later on.
      with cls._lock:
          instance = cls._unique_instances.get(id_, None)
  
          if instance is None or instance._flyweight_invalid():
              # we have no such instance yet or the existing one is invalidated,
              # so we instantiate:
              instance = type.__call__(cls, *new_args, **new_kwargs)
              cls._unique_instances[id_] = instance
          else:
              # we have an instance already that is not invalid itself; check
              # whether there is a conflict, otherwise return existing one:
              # TODO
              # Note, that this might (and probably should) go away, when we
              # decide how to deal with currently possible invalid constructor
              # calls for the repo classes. In particular this is about calling
              # it with different options than before, that might lead to
              # fundamental changes in the repository (like annex repo version
              # change or re-init of git)
  
              # force? may not mean the same thing
              msg = cls._flyweight_reject(id_, *new_args, **new_kwargs)
              if msg is not None:
                  raise InvalidInstanceRequestError(id_, msg)
      return instance
  
file_path: datalad/datalad/dataset/repo.py
imports: import logging
  import threading
  from datalad.support.exceptions import InvalidInstanceRequestError
  from datalad.support.network import RI
  from datalad import utils
label_desc: Use image node_id 3 for calling the Flyweight obj's underlying member method code with example usage: obj.__call__(cls) and returns: instance