



import re
import tempfile
import unittest
from pathlib import Path

from datasets.utils.metadata import DatasetMetadata
def   UpperCAmelCase							(		A__     )							->					str:
	_snake_case					:							Optional[Any]					     =   min(re.search("""^ +"""				,  A__     ).end() if t.startswith(""" """     ) else 0 for t in string.splitlines()     )
	return "\n".join([line[indent_level:] for line in string.splitlines() if indent_level < len(A__     )]     )


UpperCAmelCase_			      =  '''\
---
language:
- zh
- en
task_ids:
- sentiment-classification
---
# Begin of markdown

Some cool dataset card
'''

UpperCAmelCase_			      =  '''\
---
---
# Begin of markdown

Some cool dataset card
'''


UpperCAmelCase_			      =  '''\
# Begin of markdown

Some cool dataset card
'''





class 		__SCREAMING_SNAKE_CASE      (				unittest.TestCase      ):






				"""simple docstring"""

				def 		__lowerCamelCase(				self      ):






					"""simple docstring"""

					with tempfile.TemporaryDirectory() as tmp_dir:
						_snake_case					:							Tuple					     =   Path(SCREAMING_SNAKE_CASE__      ) / """README.md"""
						with open(SCREAMING_SNAKE_CASE__						,	"""w+"""      ) as readme_file:
							readme_file.write(SCREAMING_SNAKE_CASE__      )
						_snake_case					:							List[str]					     =   DatasetMetadata.from_readme(SCREAMING_SNAKE_CASE__      )
						self.assertDictEqual(SCREAMING_SNAKE_CASE__						,	{"""language""": ["""zh""", """en"""], """task_ids""": ["""sentiment-classification"""]}      )

						with open(SCREAMING_SNAKE_CASE__						,	"""w+"""      ) as readme_file:
							readme_file.write(SCREAMING_SNAKE_CASE__      )
						_snake_case					:							Any					     =   DatasetMetadata.from_readme(SCREAMING_SNAKE_CASE__      )
						self.assertDictEqual(SCREAMING_SNAKE_CASE__						,	{}      )

						with open(SCREAMING_SNAKE_CASE__						,	"""w+"""      ) as readme_file:
							readme_file.write(SCREAMING_SNAKE_CASE__      )
						_snake_case					:							Dict					     =   DatasetMetadata.from_readme(SCREAMING_SNAKE_CASE__      )
						self.assertEqual(SCREAMING_SNAKE_CASE__						,	{}      )







				def 		__lowerCamelCase(				self      ):






					"""simple docstring"""

					_snake_case					:							Tuple					     =   _dedent(
					    """\
            annotations_creators:
            - found
            language_creators:
            - found
            language:
            - en
            license:
            - unknown
            multilinguality:
            - monolingual
            pretty_name: Test Dataset
            size_categories:
            - 10K<n<100K
            source_datasets:
            - extended|other-yahoo-webscope-l6
            task_categories:
            - question-answering
            task_ids:
            - open-domain-qa
            """      )
					assert DatasetMetadata.from_yaml_string(SCREAMING_SNAKE_CASE__      )

					_snake_case					:							str					     =   _dedent(
					    """\
            annotations_creators:
            - found
            language:
            - en
            license:
            - unknown
            multilinguality:
            - monolingual
            pretty_name: Test Dataset
            size_categories:
            - 10K<n<100K
            source_datasets:
            - extended|other-yahoo-webscope-l6
            task_categories:
            - question-answering
            task_ids:
            - open-domain-qa
            task_ids:
            - open-domain-qa
            """      )
					with self.assertRaises(SCREAMING_SNAKE_CASE__      ):
						DatasetMetadata.from_yaml_string(SCREAMING_SNAKE_CASE__      )

					_snake_case					:							Tuple					     =   _dedent(
					    """\
            annotations_creators:
            - found
            language_creators:
            - found
            language:
            - en
            license:
            - unknown
            multilinguality:
            - monolingual
            pretty_name: Test Dataset
            size_categories:
            - 10K<n<100K
            source_datasets:
            - extended|other-yahoo-webscope-l6
            task_categories:
            - text-classification
            task_ids:
            - multi-class-classification
            paperswithcode_id:
            - squad
            configs:
            - en
            train-eval-index:
            - config: en
              task: text-classification
              task_id: multi_class_classification
              splits:
                train_split: train
                eval_split: test
              col_mapping:
                text: text
                label: target
              metrics:
                - type: accuracy
                  name: Accuracy
            extra_gated_prompt: |
              By clicking on “Access repository” below, you also agree to ImageNet Terms of Access:
              [RESEARCHER_FULLNAME] (the \"Researcher\") has requested permission to use the ImageNet database (the \"Database\") at Princeton University and Stanford University. In exchange for such permission, Researcher hereby agrees to the following terms and conditions:
              1. Researcher shall use the Database only for non-commercial research and educational purposes.
            extra_gated_fields:
              Company: text
              Country: text
              I agree to use this model for non-commerical use ONLY: checkbox
            """      )
					assert DatasetMetadata.from_yaml_string(SCREAMING_SNAKE_CASE__      )










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


UpperCAmelCase_			      =  '''https://github.com/huggingface/datasets/tree/main/src/datasets/utils'''
UpperCAmelCase_			      =  f"""{BASE_REF_URL}/{__file__}"""
UpperCAmelCase_			      =  logging.getLogger(__name__)
def   UpperCAmelCase							(		A__     )							->					Tuple[Any, str]:
	_snake_case					:							Optional[int]					     =   pkg_resources.read_text(A__				,  A__     )
	return yaml.safe_load(A__     ), f'''{BASE_REF_URL}/resources/{resource}'''


UpperCAmelCase_      ,				UpperCAmelCase_			      =  load_yaml_resource('''readme_structure.yaml''')

UpperCAmelCase_			      =  [
    '''[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
UpperCAmelCase_			      =  Tuple[dict, List[str], List[str]]





class 		__SCREAMING_SNAKE_CASE      :






				"""simple docstring"""

				def __init__(				self						,	SCREAMING_SNAKE_CASE__						,	SCREAMING_SNAKE_CASE__						,	SCREAMING_SNAKE_CASE__ = None						,	SCREAMING_SNAKE_CASE__ = False      ):






					"""simple docstring"""

					_snake_case					:							Optional[Any]					     =   name
					_snake_case					:							str					     =   level
					_snake_case					:							Optional[Any]					     =   lines
					_snake_case					:							Any					     =   """"""
					_snake_case					:							int					     =   True
					_snake_case					:							Dict					     =   {}
					_snake_case					:							List[str]					     =   []
					_snake_case					:							Optional[Any]					     =   []
					if self.lines is not None:
						self.parse(suppress_parsing_errors=SCREAMING_SNAKE_CASE__      )







				def 		__lowerCamelCase(				self						,	SCREAMING_SNAKE_CASE__ = False      ):






					"""simple docstring"""

					_snake_case					:							Dict					     =   """"""
					_snake_case					:							Dict					     =   []
					_snake_case					:							str					     =   False
					for line in self.lines:
						if line.strip(""" \n"""      ) == "":
							continue
						elif line.strip(""" \n"""      )[:3] == "```":
							_snake_case					:							Tuple					     =   not code_start
						elif line.split()[0] == self.level + "#" and not code_start:
							if current_sub_level != "":
								_snake_case					:							int					     =   Section(SCREAMING_SNAKE_CASE__						,	self.level + """#"""						,	SCREAMING_SNAKE_CASE__      )
								_snake_case					:							Optional[int]					     =   []
							else:
								if current_lines != []:
									self.text += "".join(SCREAMING_SNAKE_CASE__      ).strip()
									if self.text != "" and self.text not in FILLER_TEXT:
										_snake_case					:							Any					     =   False
									_snake_case					:							Tuple					     =   []

							_snake_case					:							Optional[Any]					     =   """ """.join(line.split()[1:]      ).strip(""" \n"""      )
						else:
							current_lines.append(SCREAMING_SNAKE_CASE__      )
					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.'''      )
							_snake_case					:							Union[str, Any]					     =   Section(SCREAMING_SNAKE_CASE__						,	self.level + """#"""						,	SCREAMING_SNAKE_CASE__      )
						else:
							if current_lines != []:
								self.text += "".join(SCREAMING_SNAKE_CASE__      ).strip()
								if self.text != "" and self.text not in FILLER_TEXT:
									_snake_case					:							Dict					     =   False

					if self.level == "" and not suppress_parsing_errors:
						if self.parsing_error_list != [] or self.parsing_warning_list != []:
							_snake_case					:							List[Any]					     =   """\n""".join("""-\t""" + x for x in self.parsing_error_list + self.parsing_warning_list      )
							_snake_case					:							Optional[Any]					     =   f'''The following issues were found while parsing the README at `{self.name}`:\n''' + errors
							raise ValueError(SCREAMING_SNAKE_CASE__      )







				def 		__lowerCamelCase(				self						,	SCREAMING_SNAKE_CASE__      ):






					"""simple docstring"""

					_snake_case					:							Union[str, Any]					     =   []
					_snake_case					:							Tuple					     =   []
					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
							_snake_case					:							Optional[int]					     =   [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
							_snake_case					:							Tuple					     =   [subsection["""name"""] for subsection in structure["""subsections"""]]
							_snake_case					:							Dict					     =   False
							for idx, name in enumerate(SCREAMING_SNAKE_CASE__      ):
								if name not in self.content:
									# If the expected subsection is not present
									error_list.append(f'''Section `{self.name}` is missing subsection: `{name}`.'''      )
									_snake_case					:							int					     =   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:
										_snake_case     , _snake_case     , _snake_case					:							Tuple					     =   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 		__lowerCamelCase(				self      ):






					"""simple docstring"""

					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 		__SCREAMING_SNAKE_CASE      (				lowercase__      ):  # Level 0






				"""simple docstring"""

				def __init__(				self						,	SCREAMING_SNAKE_CASE__						,	SCREAMING_SNAKE_CASE__						,	SCREAMING_SNAKE_CASE__ = None						,	SCREAMING_SNAKE_CASE__ = False      ):






					"""simple docstring"""

					super().__init__(name=SCREAMING_SNAKE_CASE__						,	level=""""""      )  # Not using lines here as we need to use a child class parse
					_snake_case					:							Optional[int]					     =   structure
					_snake_case					:							str					     =   -2
					_snake_case					:							Any					     =   0
					_snake_case					:							int					     =   lines
					if self.lines is not None:
						self.parse(suppress_parsing_errors=SCREAMING_SNAKE_CASE__      )







				def 		__lowerCamelCase(				self      ):






					"""simple docstring"""

					if self.structure is None:
						_snake_case     , _snake_case     , _snake_case					:							List[Any]					     =   self._validate(SCREAMING_SNAKE_CASE__      )
					else:
						_snake_case     , _snake_case     , _snake_case					:							str					     =   self._validate(self.structure      )
					if error_list != [] or warning_list != []:
						_snake_case					:							List[Any]					     =   """\n""".join(["""-\t""" + x for x in error_list + warning_list]      )
						_snake_case					:							Any					     =   f'''The following issues were found for the README at `{self.name}`:\n''' + errors
						raise ValueError(SCREAMING_SNAKE_CASE__      )







				@classmethod
				def 		__lowerCamelCase(				cls						,	SCREAMING_SNAKE_CASE__						,	SCREAMING_SNAKE_CASE__ = None						,	SCREAMING_SNAKE_CASE__ = False      ):






					"""simple docstring"""

					with open(SCREAMING_SNAKE_CASE__						,	encoding="""utf-8"""      ) as f:
						_snake_case					:							List[str]					     =   f.readlines()
					return cls(SCREAMING_SNAKE_CASE__						,	SCREAMING_SNAKE_CASE__						,	SCREAMING_SNAKE_CASE__						,	suppress_parsing_errors=SCREAMING_SNAKE_CASE__      )







				@classmethod
				def 		__lowerCamelCase(				cls						,	SCREAMING_SNAKE_CASE__						,	SCREAMING_SNAKE_CASE__ = None						,	SCREAMING_SNAKE_CASE__ = "root"						,	SCREAMING_SNAKE_CASE__ = False      ):






					"""simple docstring"""

					_snake_case					:							Tuple					     =   string.split("""\n"""      )
					return cls(SCREAMING_SNAKE_CASE__						,	SCREAMING_SNAKE_CASE__						,	SCREAMING_SNAKE_CASE__						,	suppress_parsing_errors=SCREAMING_SNAKE_CASE__      )







				def 		__lowerCamelCase(				self						,	SCREAMING_SNAKE_CASE__ = False      ):






					"""simple docstring"""

					_snake_case					:							Optional[int]					     =   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:
						_snake_case					:							Optional[int]					     =   self.lines[line_count + 1 :]  # Get the last + 1 th item.
					else:
						_snake_case					:							List[str]					     =   self.lines[self.tag_count :]
					super().parse(suppress_parsing_errors=SCREAMING_SNAKE_CASE__      )







				def __str__(				self      ):






					"""simple docstring"""

					return str(self.to_dict()      )







				def 		__lowerCamelCase(				self						,	SCREAMING_SNAKE_CASE__      ):






					"""simple docstring"""

					_snake_case					:							str					     =   []
					_snake_case					:							str					     =   []
					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.
					_snake_case					:							List[str]					     =   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
						_snake_case					:							List[str]					     =   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
							_snake_case     , _snake_case     , _snake_case					:							List[Any]					     =   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

						UpperCAmelCase_			      =  ArgumentParser(usage='''Validate the content (excluding YAML tags) of a README.md file.''')
						ap.add_argument('''readme_filepath''')
						UpperCAmelCase_			      =  ap.parse_args()
						UpperCAmelCase_			      =  Path(args.readme_filepath)
						UpperCAmelCase_			      =  ReadMe.from_readme(readme_filepath)





