Source code for pyglottolog.config

import pathlib
import functools
import collections

import attr
from clldutils.misc import nfilter
from clldutils.inifile import INI
from clldutils.jsonlib import load

__all__ = [
    'AES', 'AESSource', 'Macroarea', 'DocumentType', 'LanguageType', 'LanguoidLevel',
    'Generic', 'Config']


class ConfigObject(object):
    """
    Factory to turn INI file sections into instances of `@attr.s` classes.
    """
    @classmethod
    def from_section(cls, cfg, section, fname):
        try:
            fields = set(f.name for f in attr.fields(cls))
        except attr.exceptions.NotAnAttrsClassError:
            fields = None

        kw = {'name' if 'id' in cfg[section] else 'id': section}
        kw.update(cfg[section].items())
        res = cls(**{k: v for k, v in kw.items() if fields is None or k in fields})
        res._fname = fname
        return res


[docs]class Generic(ConfigObject): """ Make config options available as attributes. """ def __init__(self, **kw): for k, v in kw.items(): if v in ['True', 'False']: v = eval(v) setattr(self, k, v)
[docs]@functools.total_ordering @attr.s(cmp=False) class AES(ConfigObject): """ AES status values .. seealso:: `<https://glottolog.org/langdoc/status>`_ """ # The attribute which is used for ordering objects of this type must come first: #: Sequential numeric value ordinal = attr.ib(converter=int) #: unique identifier (suitable as Python name, \ #: see `<https://docs.python.org/3/reference/lexical_analysis.html#identifiers>`_) id = attr.ib() #: unique human-readable name name = attr.ib() #: corresponding status in the EGIDS scala egids = attr.ib() #: corresponding status in the UNESCO scala unesco = attr.ib() #: corresponding status in ElCat elcat = attr.ib() #: Glottolog reference ID linking to further information reference_id = attr.ib() icon = attr.ib(default=None) def __lt__(self, other): return self.ordinal < other.ordinal def __eq__(self, other): return self.ordinal == other.ordinal
[docs]@attr.s class AESSource(ConfigObject): """ Reference information for AES sources """ id = attr.ib() #: name = attr.ib() #: url = attr.ib() #: #: Glottolog reference ID linking to further information reference_id = attr.ib() pages = attr.ib(default=None) #:
[docs]@attr.s class Macroarea(ConfigObject): """ Glottolog macroareas (see `<https://glottolog.org/meta/glossary#macroarea>`_) """ id = attr.ib() #: name = attr.ib() #: description = attr.ib() #: #: Glottolog reference ID linking to further information reference_id = attr.ib() @property def geojson(self): fname = self._fname.parent / 'macroareas' / 'voronoi' / '{}.geojson'.format( self.name.lower().replace(' ', '_')) return load(fname) if fname.exists() else None
[docs]@attr.s class DocumentType(ConfigObject): """ Document types categorize Glottolog references """ rank = attr.ib(converter=int) #: id = attr.ib() #: name = attr.ib() #: description = attr.ib() #: abbv = attr.ib() bibabbv = attr.ib() webabbr = attr.ib() triggers = attr.ib(converter=lambda s: nfilter(s.split('\n')))
@attr.s class MEDType(ConfigObject): """ MED (aka Descriptive Status) types (more coarse-grained document types) .. seealso:: `<https://glottolog.org/langdoc/status>`_ """ rank = attr.ib(converter=int) #: id = attr.ib() #: name = attr.ib() #: description = attr.ib() #: icon = attr.ib(default=None)
[docs]@attr.s class LanguageType(ConfigObject): """ Language types categorize languages. """ id = attr.ib() #: #: Glottocode of the pseudo-family that languages of this type are grouped in. pseudo_family_id = attr.ib() category = attr.ib() #: category name for languages of this type description = attr.ib() #:
[docs]@attr.s(hash=True) class LanguoidLevel(ConfigObject): """ Languoid levels describe the position of languoid nodes in the classification. :ivar name: alias for `id` """ ordinal = attr.ib(converter=int) #: id = attr.ib() #: description = attr.ib() #: @property def name(self): return self.id
def get_ini(fname, **kw): fname = pathlib.Path(fname) if not fname.exists(): # For old-style (<=3.4) repository layout we ship the config data with pyglottolog: name = fname.name if fname.name != 'hhtype.ini' else 'document_types.ini' fname = pathlib.Path(__file__).parent / name assert fname.exists() return INI.from_file(fname, **kw)
[docs]class Config(collections.OrderedDict): """ More convenient access to objects stored as sections in INI files This class makes objects (i.e. INI sections) accessible as values of a `dict`, keyed by an `id` attribute, which is infered from the `id` or `name` option of the section and, additonally, under as attribute named after `id`. """ __defaults__ = {} @classmethod def from_ini(cls, fname, object_class): ini = get_ini(fname) d = collections.OrderedDict() for sec in ini.sections(): obj = object_class.from_section(ini, sec, fname) d[obj.id] = obj res = cls(**d) res.__defaults__ = ini['DEFAULT'] return res def __getattribute__(self, item): if item in self: return self[item] return dict.__getattribute__(self, item)
[docs] def get(self, item, default=None): if isinstance(item, str) and item in self: return self[item] if isinstance(item, ConfigObject) and getattr(item, 'id', None) in self: return self[item.id] for li in self.values(): if any(getattr(li, attr, None) == item for attr in ['name', 'value', 'description']): return li if default: return default raise ValueError(item)