




import logging
from pathlib import Path
from typing import Any, List, Tuple

import yaml


# loading package files: https://stackoverflow.com/a/20885799
try:
  import importlib.resources as pkg_resources
except ImportError:
  # Try backported to PY<37 `importlib_resources`.
  import importlib_resources as pkg_resources

from . import resources


_lowerCAmelCase    : List[str]		= "https://github.com/huggingface/datasets/tree/main/src/datasets/utils"
_lowerCAmelCase    : Tuple		= F'''{BASE_REF_URL}/{__file__}'''
_lowerCAmelCase    : Tuple		= logging.getLogger(__name__)



def     lowerCAmelCase		(		_lowerCAmelCase				: str						):




 """simple docstring"""







 UpperCAmelCase__							=    pkg_resources.read_text(_lowerCAmelCase    ,				_lowerCAmelCase						)
 return yaml.safe_load(_lowerCAmelCase						), F'''{BASE_REF_URL}/resources/{resource}'''


_lowerCAmelCase,       _lowerCAmelCase    : Tuple		= load_yaml_resource("readme_structure.yaml")

_lowerCAmelCase    : Tuple		= [
    "[Needs More Information]",
    "[More Information Needed]",
    "(https://github.com/huggingface/datasets/blob/main/CONTRIBUTING.md#how-to-contribute-to-the-dataset-cards)",
]

# Dictionary representation of section/readme, error_list, warning_list
_lowerCAmelCase    : Tuple		= Tuple[dict, List[str], List[str]]
class       _UpperCamelCase   :
      def __init__(      self :str			,   lowerCamelCase :str			,   lowerCamelCase :str			,   lowerCamelCase :List[str] = None			,   lowerCamelCase :bool = False   )  ->      Any:
       UpperCAmelCase__							=    name
       UpperCAmelCase__							=    level
       UpperCAmelCase__							=    lines
       UpperCAmelCase__							=    ""
       UpperCAmelCase__							=    True
       UpperCAmelCase__							=    {}
       UpperCAmelCase__							=    []
       UpperCAmelCase__							=    []
       if self.lines is not None:
        self.parse(suppress_parsing_errors=_a   )
      def 						UpperCAmelCase_    (      self :str			,   lowerCamelCase :bool = False   )  ->      Optional[Any]:
       UpperCAmelCase__							=    ""
       UpperCAmelCase__							=    []
       UpperCAmelCase__							=    False
       for line in self.lines:
        if line.strip(" \n"   ) == "":
         continue
        elif line.strip(" \n"   )[:3] == "```":
         UpperCAmelCase__							=    not code_start
        elif line.split()[0] == self.level + "#" and not code_start:
         if current_sub_level != "":
          UpperCAmelCase__							=    Section(_a			,   self.level + "#"			,   _a   )
          UpperCAmelCase__							=    []
         else:
          if current_lines != []:
           self.text += "".join(_a   ).strip()
           if self.text != "" and self.text not in FILLER_TEXT:
            UpperCAmelCase__							=    False
           UpperCAmelCase__							=    []

         UpperCAmelCase__							=    " ".join(line.split()[1:]   ).strip(" \n"   )
        else:
         current_lines.append(_a   )
       else:
        if current_sub_level != "":
         if current_sub_level in self.content:
          self.parsing_error_list.append(
              f'''Multiple sections with the same heading `{current_sub_level}` have been found. Please keep only one of these sections.'''   )
         UpperCAmelCase__							=    Section(_a			,   self.level + "#"			,   _a   )
        else:
         if current_lines != []:
          self.text += "".join(_a   ).strip()
          if self.text != "" and self.text not in FILLER_TEXT:
           UpperCAmelCase__							=    False

       if self.level == "" and not suppress_parsing_errors:
        if self.parsing_error_list != [] or self.parsing_warning_list != []:
         UpperCAmelCase__							=    "\n".join("-\t" + x for x in self.parsing_error_list + self.parsing_warning_list   )
         UpperCAmelCase__							=    f'''The following issues were found while parsing the README at `{self.name}`:\n''' + errors
         raise ValueError(_a   )
      def 						UpperCAmelCase_    (      self :Any			,   lowerCamelCase :dict   )  ->      List[Any]:
       UpperCAmelCase__							=    []
       UpperCAmelCase__							=    []
       if structure["allow_empty"] is False:
        # If content is expected
        if self.is_empty_text and self.content == {}:
         # If no content is found, mention it in the error_list
         error_list.append(f'''Expected some content in section `{self.name}` but it is empty.'''   )

       if structure["allow_empty_text"] is False:
        # If some text is expected
        if self.is_empty_text:
         # If no text is found, mention it in the error_list
         error_list.append(
             f'''Expected some text in section `{self.name}` but it is empty (text in subsections are ignored).'''   )
        # Subsections Validation
       if structure["subsections"] is not None:
        # If subsections are expected
        if self.content == {}:
         # If no subsections are present
         UpperCAmelCase__							=    [subsection["name"] for subsection in structure["subsections"]]
         # Mention the expected values in the error_list
         error_list.append(
             f'''Section `{self.name}` expected the following subsections: {", ".join(["`"+x+"`" for x in values]   )}. Found \'None\'.'''   )
        else:
         # If some subsections are present
         UpperCAmelCase__							=    [subsection["name"] for subsection in structure["subsections"]]
         UpperCAmelCase__							=    False
         for idx, name in enumerate(_a   ):
          if name not in self.content:
           # If the expected subsection is not present
           error_list.append(f'''Section `{self.name}` is missing subsection: `{name}`.'''   )
           UpperCAmelCase__							=    True
          else:
           # If the subsection is present, validate subsection, return the result
           # and concat the errors from subsection to section error_list

           # Skip sublevel validation if current level is `###`
           if self.level == "###":
            continue
           else:
            UpperCAmelCase__							=    self.content[name].validate(
                structure["subsections"][idx]   )
           error_list += subsec_error_list
           warning_list += subsec_warning_list

         if has_missing_subsections:  # we only allow to have extra subsections if all the other ones are here
          for name in self.content:
           if name not in structure_names:
            # If an extra subsection is present
            warning_list.append(
                f'''`{self.name}` has an extra subsection: `{name}`. Skipping further validation checks for this subsection as expected structure is unknown.'''   )
       if error_list:
        # If there are errors, do not return the dictionary as it is invalid
        return {}, error_list, warning_list
       else:
        return self.to_dict(), error_list, warning_list







      def 						UpperCAmelCase_    (      self :List[Any]   )  ->      Tuple:
       return {
           "name": self.name,
           "text": self.text,
           "is_empty_text": self.is_empty_text,
           "subsections": [value.to_dict() for value in self.content.values()],
       }



class       _UpperCamelCase   (					_a		):  # Level 0
      def __init__(      self :Any			,   lowerCamelCase :str			,   lowerCamelCase :List[str]			,   lowerCamelCase :dict = None			,   lowerCamelCase :bool = False   )  ->      Optional[int]:
       super().__init__(name=_a			,   level=""   )  # Not using lines here as we need to use a child class parse
       UpperCAmelCase__							=    structure
       UpperCAmelCase__							=    -2
       UpperCAmelCase__							=    0
       UpperCAmelCase__							=    lines
       if self.lines is not None:
        self.parse(suppress_parsing_errors=_a   )
      def 						UpperCAmelCase_    (      self :List[Any]   )  ->      Union[str, Any]:
       if self.structure is None:
        UpperCAmelCase__							=    self._validate(_a   )
       else:
        UpperCAmelCase__							=    self._validate(self.structure   )
       if error_list != [] or warning_list != []:
        UpperCAmelCase__							=    "\n".join(["-\t" + x for x in error_list + warning_list]   )
        UpperCAmelCase__							=    f'''The following issues were found for the README at `{self.name}`:\n''' + errors
        raise ValueError(_a   )
      @classmethod
      def 						UpperCAmelCase_    (      cls :List[Any]			,   lowerCamelCase :Path			,   lowerCamelCase :dict = None			,   lowerCamelCase :bool = False   )  ->      str:
       with open(_a			,   encoding="utf-8"   ) as f:
        UpperCAmelCase__							=    f.readlines()
       return cls(_a			,   _a			,   _a			,   suppress_parsing_errors=_a   )
      @classmethod
      def 						UpperCAmelCase_    (      cls :Optional[int]			,   lowerCamelCase :str			,   lowerCamelCase :dict = None			,   lowerCamelCase :str = "root"			,   lowerCamelCase :bool = False   )  ->      str:
       UpperCAmelCase__							=    string.split("\n"   )
       return cls(_a			,   _a			,   _a			,   suppress_parsing_errors=_a   )
      def 						UpperCAmelCase_    (      self :Optional[Any]			,   lowerCamelCase :bool = False   )  ->      List[str]:
       # Skip Tags
       UpperCAmelCase__							=    0

       for line in self.lines:
        self.yaml_tags_line_count += 1
        if line.strip(" \n"   ) == "---":
         self.tag_count += 1
         if self.tag_count == 2:
          break
        line_count += 1
       if self.tag_count == 2:
        UpperCAmelCase__							=    self.lines[line_count + 1 :]  # Get the last + 1 th item.
       else:
        UpperCAmelCase__							=    self.lines[self.tag_count :]
       super().parse(suppress_parsing_errors=_a   )
      def __str__(      self :Tuple   )  ->      int:
       return str(self.to_dict()   )







      def 						UpperCAmelCase_    (      self :Dict			,   lowerCamelCase :Union[str, Any]   )  ->      Dict:
       UpperCAmelCase__							=    []
       UpperCAmelCase__							=    []
       if self.yaml_tags_line_count == 0:
        warning_list.append("Empty YAML markers are present in the README."   )
       elif self.tag_count == 0:
        warning_list.append("No YAML markers are present in the README."   )
       elif self.tag_count == 1:
        warning_list.append("Only the start of YAML tags present in the README."   )
       # Check how many first level sections are present.
       UpperCAmelCase__							=    len(self.content.keys()   )
       if num_first_level_keys > 1:
        # If more than one, add to the error list, continue
        error_list.append(
            f'''The README has several first-level headings: {", ".join(["`"+x+"`" for x in list(self.content.keys()   )]   )}. Only one heading is expected. Skipping further validation for this README.'''   )
       elif num_first_level_keys < 1:
        # If less than one, append error.
        error_list.append(
            "The README has no first-level headings. One heading is expected. Skipping further validation for this README."   )

       else:
        # If one exactly
        UpperCAmelCase__							=    list(self.content.keys()   )[0]  # Get the key
        if start_key.startswith("Dataset Card for"   ):  # Check correct start
         # If the starting is correct, validate all the sections
         UpperCAmelCase__							=    self.content[start_key].validate(
             readme_structure["subsections"][0]   )
         error_list += sec_error_list
         warning_list += sec_warning_list
        else:
         # If not found, append error
         error_list.append(
             "No first-level heading starting with `Dataset Card for` found in README. Skipping further validation for this README."   )
       if error_list:
        # If there are errors, do not return the dictionary as it is invalid
        return {}, error_list, warning_list
       else:
        return self.to_dict(), error_list, warning_list


if __name__ == "__main__":
  from argparse import ArgumentParser

  _lowerCAmelCase    : Union[str, Any]		= ArgumentParser(usage="Validate the content (excluding YAML tags) of a README.md file.")
  ap.add_argument("readme_filepath")
  _lowerCAmelCase    : Any		= ap.parse_args()
  _lowerCAmelCase    : int		= Path(args.readme_filepath)
  _lowerCAmelCase    : int		= ReadMe.from_readme(readme_filepath)










from typing import Callable





def     __lowerCamelCase							(   __magic_name__						:	Callable    ):

							def wrapper(__magic_name__						:	Optional[Any]    ):
														a__:							List[Any]									=function_with_docstring.__doc__
														return target_function

							return wrapper





