<commit_before>"""
This file contains the RunstatLoader class that is used to dynamically
create Runstat Table and View objects from a <dict> of data.  The <dict> can
originate from any kind of source: YAML, JSON, program.  For examples of YAML
refer to the .yml files in this jnpr.junos.op directory.
"""
# stdlib
from copy import deepcopy

# locally
from . import RunstatMaker as _RSM

__all__ = ['RunstatLoader']

# internally used shortcuts

_VIEW = _RSM.View
_FIELDS = _RSM.Fields
_GET = _RSM.GetTable 
_TABLE = _RSM.Table 

class RunstatLoader(object):
  """
  Used to load a <dict> of data that contains Table and View definitions.

  The primary method is :load(): which will return a <dict> of item-name and
  item-class definitions.  

  If you want to import these definitions directly into your namespace, (like a module)
  you would do the following:

    loader = RunstatLoader()
    catalog = loader.load( <catalog_dict> )
    globals().update( catalog )

  If you did not want to do this, you can access the items as the catalog.  For 
  example, if your <catalog_dict> contained a Table called MyTable, then you could do
  something like:

    MyTable = catalog['MyTable']
    table = MyTable(dev)
    table.get()
    ...
  """  
  def __init__(self):
    self._catalog_dict = None       # YAML data

    self._item_gettables = []    # list of the get-tables
    self._item_views = []        # list of views to build
    self._item_tables = []       # list of tables to build

    self.catalog = {}           # catalog of built classes

  ##### -----------------------------------------------------------------------
  ##### Create a View class from YAML definition
  ##### -----------------------------------------------------------------------

  def _fieldfunc_True(self, value_rhs):
    return lambda x: x == value_rhs

  def _fieldfunc_False(self, value_rhs):
    return lambda x: x != value_rhs

  def _add_dictfield(self, fields, f_name, f_dict, kvargs ):
    """ add a field based on its associated dictionary """
    # at present if a field is a <dict> then there is **one
    # item** - { the xpath value : the option control }.  typically
    # the option would be a bultin class type like 'int'
    # however, as this framework expands in capability, this
    # will be enhaced, yo!

    xpath, opt = f_dict.items()[0]       # get first/only key,value
    if 'flag' == opt: opt = 'bool'       # flag is alias for bool

    # first check to see if the option is a built-in Python
    # type, most commonly would be 'int' for numbers, like counters

    astype = __builtins__.get(opt)
    if astype is not None:
      kvargs['astype'] = astype
      fields.astype( f_name, xpath, **kvargs)
      return

    # next check to see if this is a "field-function"
    # operator in the form "func=value", like "True=enabled"

    if isinstance(opt,str) and opt.find('=') > 0:
      field_cmd,value_rhs = opt.split('=')
      fn_field = '_fieldfunc_' + field_cmd
      if not hasattr(self, fn_field): 
        raise ValueError("Unknown field-func: '%'" % field_cmd)
      kvargs['astype'] = getattr(self,fn_field)(value_rhs)
      fields.astype( f_name, xpath, **kvargs )
      return

    raise RuntimeError("Dont know what to do with field: '%s'" % f_name)

  # ---[ END: _add_dictfield ] ------------------------------------------------

  def _add_view_fields(self, view_dict, fields_name, fields):
    """ add a group of fields to the view """
    fields_dict = view_dict[fields_name]
    try:
      # see if this is a 'fields_<group>' collection, and if so
      # then we automatically setup using the group mechanism
      mark = fields_name.index('_')
      group = {'group':fields_name[mark+1:]}
    except:
      # otherwise, no group, just standard 'fields'
      group={}

    for f_name, f_data in fields_dict.items():  
      # each field could have its own unique set of properties
      # so create a kvargs <dict> each time.  but copy in the
      # groups <dict> (single item) generically.
      kvargs = {}
      kvargs.update(group)

      if isinstance(f_data,dict):
        self._add_dictfield( fields, f_name, f_data, kvargs )
        continue

      if f_data in self._catalog_dict:
        # f_data is the table name
        cls_tbl = self.catalog.get(f_data, self._build_table( f_data ))
        fields.table( f_name, cls_tbl )
        continue        

      # if we are here, then it means that the field is a string value
      xpath = f_name if f_data is True else f_data
      fields.str(f_name,xpath,**kvargs)

  ### -------------------------------------------------------------------------

  def _build_view(self, view_name):
    """ build a new View definition """
    if view_name in self.catalog: return self.catalog[view_name]

    view_dict = self._catalog_dict[view_name]
    kvargs = { 'view_name' : view_name }

    # if there are field groups, then get that now.
    if 'groups' in view_dict: 
      kvargs['groups'] = view_dict['groups']

    fields = _FIELDS()
    fg_list = [name for name in view_dict if name.startswith('fields')]
    for fg_name in fg_list: 
      self._add_view_fields( view_dict, fg_name, fields )

    cls = _VIEW( fields.end, **kvargs )
    self.catalog[view_name] = cls
    return cls

  ##### -----------------------------------------------------------------------
  ##### Create a Get-Table from YAML definition<commit_msg>added "extends" to YAML view builder<commit_after>"""
This file contains the RunstatLoader class that is used to dynamically
create Runstat Table and View objects from a <dict> of data.  The <dict> can
originate from any kind of source: YAML, JSON, program.  For examples of YAML
refer to the .yml files in this jnpr.junos.op directory.
"""
# stdlib
from copy import deepcopy

# locally
from . import RunstatMaker as _RSM

__all__ = ['RunstatLoader']

# internally used shortcuts

_VIEW = _RSM.View
_FIELDS = _RSM.Fields
_GET = _RSM.GetTable 
_TABLE = _RSM.Table 

class RunstatLoader(object):
  """
  Used to load a <dict> of data that contains Table and View definitions.

  The primary method is :load(): which will return a <dict> of item-name and
  item-class definitions.  

  If you want to import these definitions directly into your namespace, (like a module)
  you would do the following:

    loader = RunstatLoader()
    catalog = loader.load( <catalog_dict> )
    globals().update( catalog )

  If you did not want to do this, you can access the items as the catalog.  For 
  example, if your <catalog_dict> contained a Table called MyTable, then you could do
  something like:

    MyTable = catalog['MyTable']
    table = MyTable(dev)
    table.get()
    ...
  """  
  def __init__(self):
    self._catalog_dict = None       # YAML data

    self._item_gettables = []    # list of the get-tables
    self._item_views = []        # list of views to build
    self._item_tables = []       # list of tables to build

    self.catalog = {}           # catalog of built classes

  ##### -----------------------------------------------------------------------
  ##### Create a View class from YAML definition
  ##### -----------------------------------------------------------------------

  def _fieldfunc_True(self, value_rhs):
    return lambda x: x == value_rhs

  def _fieldfunc_False(self, value_rhs):
    return lambda x: x != value_rhs

  def _add_dictfield(self, fields, f_name, f_dict, kvargs ):
    """ add a field based on its associated dictionary """
    # at present if a field is a <dict> then there is **one
    # item** - { the xpath value : the option control }.  typically
    # the option would be a bultin class type like 'int'
    # however, as this framework expands in capability, this
    # will be enhaced, yo!

    xpath, opt = f_dict.items()[0]       # get first/only key,value
    if 'flag' == opt: opt = 'bool'       # flag is alias for bool

    # first check to see if the option is a built-in Python
    # type, most commonly would be 'int' for numbers, like counters

    astype = __builtins__.get(opt)
    if astype is not None:
      kvargs['astype'] = astype
      fields.astype( f_name, xpath, **kvargs)
      return

    # next check to see if this is a "field-function"
    # operator in the form "func=value", like "True=enabled"

    if isinstance(opt,str) and opt.find('=') > 0:
      field_cmd,value_rhs = opt.split('=')
      fn_field = '_fieldfunc_' + field_cmd
      if not hasattr(self, fn_field): 
        raise ValueError("Unknown field-func: '%'" % field_cmd)
      kvargs['astype'] = getattr(self,fn_field)(value_rhs)
      fields.astype( f_name, xpath, **kvargs )
      return

    raise RuntimeError("Dont know what to do with field: '%s'" % f_name)

  # ---[ END: _add_dictfield ] ------------------------------------------------

  def _add_view_fields(self, view_dict, fields_name, fields):
    """ add a group of fields to the view """
    fields_dict = view_dict[fields_name]
    try:
      # see if this is a 'fields_<group>' collection, and if so
      # then we automatically setup using the group mechanism
      mark = fields_name.index('_')
      group = {'group':fields_name[mark+1:]}
    except:
      # otherwise, no group, just standard 'fields'
      group={}

    for f_name, f_data in fields_dict.items():  
      # each field could have its own unique set of properties
      # so create a kvargs <dict> each time.  but copy in the
      # groups <dict> (single item) generically.
      kvargs = {}
      kvargs.update(group)

      if isinstance(f_data,dict):
        self._add_dictfield( fields, f_name, f_data, kvargs )
        continue

      if f_data in self._catalog_dict:
        # f_data is the table name
        cls_tbl = self.catalog.get(f_data, self._build_table( f_data ))
        fields.table( f_name, cls_tbl )
        continue        

      # if we are here, then it means that the field is a string value
      xpath = f_name if f_data is True else f_data
      fields.str(f_name,xpath,**kvargs)

  ### -------------------------------------------------------------------------

  def _build_view(self, view_name):
    """ build a new View definition """
    if view_name in self.catalog: return self.catalog[view_name]

    view_dict = self._catalog_dict[view_name]
    kvargs = { 'view_name' : view_name }

    # if there are field groups, then get that now.
    if 'groups' in view_dict: kvargs['groups'] = view_dict['groups']

    # if this view extends another ...
    if 'extends' in view_dict:
      base_cls =  self.catalog.get(view_dict['extends'])
      # @@@ should check for base_cls is None!
      kvargs['extends'] = base_cls

    fields = _FIELDS()
    fg_list = [name for name in view_dict if name.startswith('fields')]
    for fg_name in fg_list: 
      self._add_view_fields( view_dict, fg_name, fields )

    cls = _VIEW( fields.end, **kvargs )
    self.catalog[view_name] = cls
    return cls

  ##### -----------------------------------------------------------------------
  ##### Create a Get-Table from YAML definition
{'fraction_non_alphanumeric': 0.15391571712787122, 'fraction_numerical': 0.0005425935973955507, 'mean_word_length': 2.318031803180318, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6059168', 'n_tokens_mistral': 3304, 'n_tokens_neox': 3224, 'n_words': 1161}