class_docstr: Abstract Parser class to be inherited by all StreamAlert Parsers
class_docstr_tok: ["Abstract","Parser","class","to","be","inherited","by","all","StreamAlert","Parsers"]
docstr: Verify the declared schema matches the record
  
  Args:
      record (dict): Parsed record value
      schema (dict): Schema for the data being parsed. This could be parsing
          a nested value, so it should be passed in as an argument.
      optionals (set=None): Set of optional keys in the passed in schema that
          should be excluded from the key checking.
      is_envelope (bool=False): Set to True if this is validating the envelope
          keys and not the parsed record keys. Envelope keys only have to be a
          subset of the entire envelope, so simply check for that.
  
  Returns:
      bool: True if the log matches the schema, False if not
docstr_tok: ["Verify","the","declared","schema","matches","the","record","Args",":","record","(","dict",")",":","Parsed","record","value","schema","(","dict",")",":","Schema","for","the","data","being","parsed",".","This","could","be","parsing","a","nested","value",",","so","it","should","be","passed","in","as","an","argument",".","optionals","(","set=None",")",":","Set","of","optional","keys","in","the","passed","in","schema","that","should","be","excluded","from","the","key","checking",".","is_envelope","(","bool=False",")",":","Set","to","True","if","this","is","validating","the","envelope","keys","and","not","the","parsed","record","keys",".","Envelope","keys","only","have","to","be","a","subset","of","the","entire","envelope",",","so","simply","check","for","that",".","Returns",":","bool",":","True","if","the","log","matches","the","schema",",","False","if","not"]
code: def _key_check(
      cls, record, schema, optionals=None, is_envelope=False
  ):
      """Verify the declared schema matches the record
  
      Args:
          record (dict): Parsed record value
          schema (dict): Schema for the data being parsed. This could be parsing
              a nested value, so it should be passed in as an argument.
          optionals (set=None): Set of optional keys in the passed in schema that
              should be excluded from the key checking.
          is_envelope (bool=False): Set to True if this is validating the envelope
              keys and not the parsed record keys. Envelope keys only have to be a
              subset of the entire envelope, so simply check for that.
  
      Returns:
          bool: True if the log matches the schema, False if not
      """
      # Nothing to do for empty schema. This happens in the case of nested schema validation
      if not schema:
          return True
  
      # Expect the record is a dict. Return False (schema doesn't match) if it is not.
      if not isinstance(record, dict):
          return False
  
      schema_keys = set(schema)
  
      keys = (
          set(record) if not optionals else set(record).union(optionals)
      )
  
      if is_envelope and not schema_keys.issubset(keys):
          LOGGER.debug(
              "Missing keys in record envelope: %s", schema_keys - keys
          )
          return False
  
      if not is_envelope and keys != schema_keys:
          expected = schema_keys - keys
          if expected:
              LOGGER.debug(
                  "Expected keys not found in record: %s",
                  ", ".join(
                      str(val) for val in sorted(expected, key=str)
                  ),
              )
  
          found = keys - schema_keys
          if found:
              LOGGER.debug(
                  "Found keys not expected in record: %s",
                  ", ".join(str(val) for val in sorted(found, key=str)),
              )
          return False
  
      # Nested key check
      match = True
      for key, key_type in schema.items():
          # Skip any value that is not a dictionary
          if not isinstance(key_type, dict):
              continue
  
          # Nested key check, these cannot support optionals currently
          # Use a default dict value in case this key is optional
          match = match and cls._key_check(
              record.get(key, {}), key_type
          )
  
      if not match and LOGGER_DEBUG_ENABLED:
          LOGGER.debug(
              "Nested key check failure. Schema:\n%s\nRecord:\n%s",
              json.dumps(schema, indent=2, sort_keys=True),
              json.dumps(record, indent=2, sort_keys=True),
          )
  
      return match
  
imports: from abc import ABCMeta, abstractmethod
  from copy import deepcopy
  import csv
  from fnmatch import fnmatch
  import json
  import logging
  import re
  import io
  import jmespath
  from streamalert.shared.logger import get_logger
label_desc: Use image node_id 20 for calling the ParserBase obj's underlying member method code with example usage: obj._key_check(cls, record, schema, optionals, is_envelope) and returns: match, True, False, False, False