def process(self):  # pragma: no cover
        """
        Process the logic and structuration of the mining database.
        """

        if PyFunceble.CONFIGURATION["mining"]:
            # The mining is activated.

            # We load the mining logic.
            mined = self.mine()

            if mined:
                # The mined data is not empty or None.

                # We add the mined data to the global database.
                self._add(mined)

                # And we finally backup everything.
                self._backup()

def process ( self ) : if PyFunceble . CONFIGURATION [ "mining" ] : mined = self . mine ( ) if mined : self . _add ( mined ) self . _backup ( )

Process the logic and structuration of the mining database .


def _get_content(cls, file):
        """
        Get and return the content of the given log file.

        :param file: The file we have to get the content from.
        :type file: str

        :return The content of the given file.
        :rtype: dict
        """

        if PyFunceble.path.isfile(file):
            return Dict().from_json(File(file).read())

        return {}

def _get_content ( cls , file ) : if PyFunceble . path . isfile ( file ) : return Dict ( ) . from_json ( File ( file ) . read ( ) ) return { }

Get and return the content of the given log file .


def _write_content(cls, content, file):
        """
        Write the content into the given file.

        :param content: The dict to write.
        :type content: dict

        :param file: The file to write.
        :type file: str
        """

        if not PyFunceble.CONFIGURATION["no_files"]:
            if not isinstance(content, dict):
                content = {}

            Dict(content).to_json(file)

def _write_content ( cls , content , file ) : if not PyFunceble . CONFIGURATION [ "no_files" ] : if not isinstance ( content , dict ) : content = { } Dict ( content ) . to_json ( file )


def whois(self, record):
        """
        Logs the WHOIS record if needed.

        :param record: The record to log.
        :type record: str
        """

        if PyFunceble.CONFIGURATION["debug"] and PyFunceble.CONFIGURATION["logs"]:
            # The debug and the logs subsystem are activated.

            if PyFunceble.INTERN["referer"]:
                referer = PyFunceble.INTERN["referer"]
            else:
                referer = None

            to_write = {
                self.current_time: {
                    "domain": PyFunceble.INTERN["to_test"],
                    "record": record,
                    "referer": referer,
                }
            }

            if self.output:
                output = self.output
            else:
                output = PyFunceble.OUTPUT_DIRECTORY
                output += PyFunceble.OUTPUTS["parent_directory"]
                output += PyFunceble.OUTPUTS["logs"]["directories"]["parent"]
                output += PyFunceble.OUTPUTS["logs"]["filenames"]["whois"]

            current_content = self._get_content(output)
            current_content.update(to_write)

            self._write_content(current_content, output)

def whois ( self , record ) : if PyFunceble . CONFIGURATION [ "debug" ] and PyFunceble . CONFIGURATION [ "logs" ] : if PyFunceble . INTERN [ "referer" ] : referer = PyFunceble . INTERN [ "referer" ] else : referer = None to_write = { self . current_time : { "domain" : PyFunceble . INTERN [ "to_test" ] , "record" : record , "referer" : referer , } } if self . output : output = self . output else : output = PyFunceble . OUTPUT_DIRECTORY output += PyFunceble . OUTPUTS [ "parent_directory" ] output += PyFunceble . OUTPUTS [ "logs" ] [ "directories" ] [ "parent" ] output += PyFunceble . OUTPUTS [ "logs" ] [ "filenames" ] [ "whois" ] current_content = self . _get_content ( output ) current_content . update ( to_write ) self . _write_content ( current_content , output )


def expiration_date(self, extracted):
        """
        Logs the extracted expiration date.

        :param extracted: The extracted expiration date (from WHOIS record).
        :type extracted: str
        """

        if PyFunceble.CONFIGURATION["logs"]:
            # The logs subsystem is activated.

            if PyFunceble.INTERN["referer"]:
                referer = PyFunceble.INTERN["referer"]
            else:
                referer = None

            to_write = {
                self.current_time: {
                    "domain": PyFunceble.INTERN["to_test"],
                    "expiration_date": extracted,
                    "whois_server": referer,
                }
            }

            if self.output:
                output = self.output
            else:
                output = PyFunceble.OUTPUT_DIRECTORY
                output += PyFunceble.OUTPUTS["parent_directory"]
                output += PyFunceble.OUTPUTS["logs"]["directories"]["parent"]
                output += PyFunceble.OUTPUTS["logs"]["filenames"]["date_format"]

            current_content = self._get_content(output)
            current_content.update(to_write)

            self._write_content(current_content, output)

            if PyFunceble.CONFIGURATION["share_logs"]:
                # The logs sharing is activated.

                # And we share the logs with the api.
                PyFunceble.requests.post(
                    PyFunceble.LINKS["api_date_format"],
                    data=to_write[self.current_time],
                )

def expiration_date ( self , extracted ) : if PyFunceble . CONFIGURATION [ "logs" ] : if PyFunceble . INTERN [ "referer" ] : referer = PyFunceble . INTERN [ "referer" ] else : referer = None to_write = { self . current_time : { "domain" : PyFunceble . INTERN [ "to_test" ] , "expiration_date" : extracted , "whois_server" : referer , } } if self . output : output = self . output else : output = PyFunceble . OUTPUT_DIRECTORY output += PyFunceble . OUTPUTS [ "parent_directory" ] output += PyFunceble . OUTPUTS [ "logs" ] [ "directories" ] [ "parent" ] output += PyFunceble . OUTPUTS [ "logs" ] [ "filenames" ] [ "date_format" ] current_content = self . _get_content ( output ) current_content . update ( to_write ) self . _write_content ( current_content , output ) if PyFunceble . CONFIGURATION [ "share_logs" ] : PyFunceble . requests . post ( PyFunceble . LINKS [ "api_date_format" ] , data = to_write [ self . current_time ] , )


def referer_not_found(self, extension):
        """
        Logs the case that the referer was not found.

        :param extension: The extension of the domain we are testing.
        :type extension: str
        """

        if PyFunceble.CONFIGURATION["logs"]:
            # The logs subsystem is activated.

            to_write = {
                self.current_time: {
                    "domain": PyFunceble.INTERN["to_test"],
                    "extension": extension,
                }
            }

            if self.output:
                output = self.output
            else:
                output = PyFunceble.OUTPUT_DIRECTORY
                output += PyFunceble.OUTPUTS["parent_directory"]
                output += PyFunceble.OUTPUTS["logs"]["directories"]["parent"]
                output += PyFunceble.OUTPUTS["logs"]["filenames"]["no_referer"]

            current_content = self._get_content(output)
            current_content.update(to_write)

            self._write_content(current_content, output)

            if PyFunceble.CONFIGURATION["share_logs"]:
                # The logs sharing is activated.

                # And we share the logs with the api.
                PyFunceble.requests.post(
                    PyFunceble.LINKS["api_no_referer"], data=to_write[self.current_time]
                )

def referer_not_found ( self , extension ) : if PyFunceble . CONFIGURATION [ "logs" ] : to_write = { self . current_time : { "domain" : PyFunceble . INTERN [ "to_test" ] , "extension" : extension , } } if self . output : output = self . output else : output = PyFunceble . OUTPUT_DIRECTORY output += PyFunceble . OUTPUTS [ "parent_directory" ] output += PyFunceble . OUTPUTS [ "logs" ] [ "directories" ] [ "parent" ] output += PyFunceble . OUTPUTS [ "logs" ] [ "filenames" ] [ "no_referer" ] current_content = self . _get_content ( output ) current_content . update ( to_write ) self . _write_content ( current_content , output ) if PyFunceble . CONFIGURATION [ "share_logs" ] : PyFunceble . requests . post ( PyFunceble . LINKS [ "api_no_referer" ] , data = to_write [ self . current_time ] )

Logs the case that the referer was not found .


def _before_header(self):
        """
        Print informations about PyFunceble and the date of generation of a file
        into a given path, if doesn't exist.
        """

        if (
            not PyFunceble.CONFIGURATION["no_files"]
            and self.output
            and not PyFunceble.path.isfile(self.output)
        ):
            # * We are allowed to generate files.
            # and
            # * And output is given.
            # and
            # * The given output does not exist.

            # We initiate the information about what generated the file.
            link = "# File generated by %s\n" % PyFunceble.LINKS["repo"]

            # We initiate the information about the generation date of this file.
            date_of_generation = (
                "# Date of generation: %s \n\n" % PyFunceble.CURRENT_TIME
            )

            # We initiate a variable which will save the list of
            # templates which have to meet in order to write the before
            # header informations.
            authorized_templates = [
                "Generic_File",
                PyFunceble.STATUS["official"]["up"],
                PyFunceble.STATUS["official"]["down"],
                PyFunceble.STATUS["official"]["invalid"],
                PyFunceble.STATUS["official"]["valid"],
                "Less",
            ]

            if self.template in authorized_templates:
                # The current header is in our list of authorized templated.

                # We get the header.
                header = (
                    self._header_constructor(self.currently_used_header, None)[0] + "\n"
                )

            try:
                # We try to print the link, the date of generation and the header in the
                # given file.
                File(self.output).write(link + date_of_generation + header)
            except UnboundLocalError:
                # We don't have any header.

                # We print the link and the date in the given file.
                File(self.output).write(link + date_of_generation)

def
_before_header
(
self
)
:
if
(
not
PyFunceble
.
CONFIGURATION
[
"no_files"
]
and
self
.
output
and
not
PyFunceble
.
path
.
isfile
(
self
.
output
)
)
:
link
=
"# File generated by %s\n"
%
PyFunceble
.
LINKS
[
"repo"
]
date_of_generation
=
(
"# Date of generation: %s \n\n"
%
PyFunceble
.
CURRENT_TIME
)
authorized_templates
=
[
"Generic_File"
,
PyFunceble
.
STATUS
[
"official"
]
[
"up"
]
,
PyFunceble
.
STATUS
[
"official"
]
[
"down"
]
,
PyFunceble
.
STATUS
[
"official"
]
[
"invalid"
]
,
PyFunceble
.
STATUS
[
"official"
]
[
"valid"
]
,
"Less"
,
]
if
self
.
template
in
authorized_templates
:
header
=
(
self
.
_header_constructor
(
self
.
currently_used_header
,
)
[
0
]
+
"\n"
)
try
:
File
(
self
.
output
)
.
write
(
link
+
date_of_generation
+
header
)
except
UnboundLocalError
:
File
(
self
.
output
)
.
write
(
link
+
date_of_generation
)

Print informations about PyFunceble and the date of generation of a file into a given path if doesn t exist .


def _header_constructor(
        cls, data_to_print, header_separator="-", column_separator=" "
    ):
        """
        Construct header of the table according to template.

        :param data_to_print:
            The list of data to print into the header of the table.
        :type data_to_print: list

        :param header_separator:
            The separator to use between the table header and our data.
        :type header_separator: str

        :param colomn_separator: The separator to use between each colomns.
        :type colomn_separator: str

        :return: The data to print in a list format.
        :rtype: list
        """

        # We initiate a variable which will save the header data.
        header_data = []

        # We initiate a variable which will save the header sizes.
        header_size = ""

        # We initiate the glue to set before the size.
        before_size = "%-"

        # We initiate the glue to set after the size.
        after_size = "s"

        if header_separator:
            # The header separator is not empty.

            # We initiate a variable which will save the list of
            # separator data.
            header_separator_data = []

        # We get the length of the data to print.
        length_data_to_print = len(data_to_print) - 1

        # We initiate an iterator.
        i = 0

        for data in data_to_print:
            # We loop through the list of data.

            # We get the size of the currently read data.
            size = data_to_print[data]

            # We append the data to the header data list.
            header_data.append(data)

            # We construct the header size.
            # Note: our header size is formatted line %s-sizes
            # (the s at the end is part of the formatting.)
            header_size += before_size + str(size) + after_size

            if i < length_data_to_print:
                # The iterator is less than the length of data to print.

                # We append the the colomn separator to the header size.
                header_size += column_separator

            if header_separator:
                # The header separator is given.

                # We append the right size of separator to the list of
                # separator data.
                header_separator_data.append(header_separator * size)

            # We increase the iterator.
            i += 1

        if header_separator:
            # The header separator is given.

            return [
                # We return the formatted header (like we will do with print('%s' % 'hello'))
                header_size % tuple(header_data),
                # We return the formatted header separator.
                header_size % tuple(header_separator_data),
            ]

        # The header separator is not given.

        # We return the formetted header.
        return [header_size % tuple(header_data)]

def
_header_constructor
(
cls
,
data_to_print
,
header_separator
=
"-"
,
column_separator
=
" "
)
:
header_data
=
[
]
header_size
=
""
before_size
=
"%-"
after_size
=
"s"
if
header_separator
:
header_separator_data
=
[
]
length_data_to_print
=
len
(
data_to_print
)
1
i
=
0
for
data
in
data_to_print
:
size
=
data_to_print
[
data
]
header_data
.
append
(
data
)
header_size
+=
before_size
+
str
(
size
)
+
after_size
if
i
<
length_data_to_print
:
header_size
+=
column_separator
if
header_separator
:
header_separator_data
.
append
(
header_separator
*
size
)
i
+=
1
if
header_separator
:
return
[
header_size
%
tuple
(
header_data
)
,
header_size
%
tuple
(
header_separator_data
)
,
]
return
[
header_size
%
tuple
(
header_data
)
]

Construct header of the table according to template .


def header(
        self, do_not_print=False
    ):  # pragma: no cover pylint: disable=too-many-branches
        """
        Management and creation of templates of header.
        Please consider as "header" the title of each columns.

        :param do_not_print:
            Tell us if we have to print the header or not.
        :type do_not_print: bool
        """

        if (
            not PyFunceble.CONFIGURATION["header_printed"]
            or self.template == "Percentage"
            or do_not_print
        ):
            # * The header has not been already printed.
            # or
            # * The template is the `Percentage template`.
            # or
            # * We are authorized to print something.

            if (
                self.template.lower() in PyFunceble.STATUS["list"]["generic"]
                or self.template == "Generic_File"
            ):
                # * The template is into the list of generic status.
                # or
                # * The template is equal to `Generic_File`.

                # The data to print is the Generic header.
                to_print = self.headers["Generic"]

                if (
                    self.template.lower() in PyFunceble.STATUS["list"]["generic"]
                    and PyFunceble.HTTP_CODE["active"]
                ):
                    # * The template is in the list of generic status.
                    # and
                    # * the http status code extraction is activated.

                    # We remove the Analyze Date colomn from the data to print.
                    to_print = Dict(to_print).remove_key("Analyze Date")
            elif self.template.lower() in PyFunceble.STATUS["list"]["up"]:
                # The template is in the list of up status.

                # We informations to print is the up header.
                to_print = self.headers[PyFunceble.STATUS["official"]["up"]]
            elif self.template.lower() in PyFunceble.STATUS["list"]["valid"]:
                # The template is in the list of valid status.

                # We informations to print is the valid header.
                to_print = self.headers[PyFunceble.STATUS["official"]["valid"]]
            elif self.template.lower() in PyFunceble.STATUS["list"]["down"]:
                # The template is in the list of down status.

                # We informations to print is the down header.
                to_print = self.headers[PyFunceble.STATUS["official"]["down"]]
            elif self.template.lower() in PyFunceble.STATUS["list"]["invalid"]:
                # The template is in the list of invalid status.

                # We informations to print is the invalid header.
                to_print = self.headers[PyFunceble.STATUS["official"]["invalid"]]
            elif (
                self.template == "Less"
                or self.template == "Percentage"
                or self.template == "HTTP"
            ):  # pylint: disable=line-too-long
                # * The template is equal to `Less`.
                # or
                # * The template is equal to `Percentage`.
                # or
                # * The template is equal to `HTTP`.

                # We get the header with the help of the template name.
                to_print = self.headers[self.template]

                if self.template == "Less" and not PyFunceble.HTTP_CODE["active"]:
                    # * The template is equal to `Less`.
                    # and
                    # * The http status code extraction is deactivated.

                    # We append the source index to the header.
                    to_print["Source"] = 10

            if not PyFunceble.HTTP_CODE["active"]:
                # * The http status code extraction is deactivated.

                # We remove the HTTP Code index from the data to print.
                to_print = Dict(to_print).remove_key("HTTP Code")

            # We update the currently used header.
            self.currently_used_header = to_print

            if not do_not_print:
                # We are not authorized to print anything.

                # We generate the before header.
                self._before_header()

                for formatted_template in self._header_constructor(to_print):
                    # We loop through the formatted template.

                    if not self.only_on_file:
                        # We do not have to print only on file.

                        # We print on screen the formatted header template.
                        print(formatted_template)

                    if not PyFunceble.CONFIGURATION["no_files"] and self.output:
                        # An output destination is given.

                        # We write the file with the formatted header template.
                        File(self.output).write(formatted_template + "\n")

def
header
(
self
,
do_not_print
=
False
)
:
if
(
not
PyFunceble
.
CONFIGURATION
[
"header_printed"
]
or
self
.
template
==
"Percentage"
or
do_not_print
)
:
if
(
self
.
template
.
lower
(
)
in
PyFunceble
.
STATUS
[
"list"
]
[
"generic"
]
or
self
.
template
==
"Generic_File"
)
:
to_print
=
self
.
headers
[
"Generic"
]
if
(
self
.
template
.
lower
(
)
in
PyFunceble
.
STATUS
[
"list"
]
[
"generic"
]
and
PyFunceble
.
HTTP_CODE
[
"active"
]
)
:
to_print
=
Dict
(
to_print
)
.
remove_key
(
"Analyze Date"
)
elif
self
.
template
.
lower
(
)
in
PyFunceble
.
STATUS
[
"list"
]
[
"up"
]
:
to_print
=
self
.
headers
[
PyFunceble
.
STATUS
[
"official"
]
[
"up"
]
]
elif
self
.
template
.
lower
(
)
in
PyFunceble
.
STATUS
[
"list"
]
[
"valid"
]
:
to_print
=
self
.
headers
[
PyFunceble
.
STATUS
[
"official"
]
[
"valid"
]
]
elif
self
.
template
.
lower
(
)
in
PyFunceble
.
STATUS
[
"list"
]
[
"down"
]
:
to_print
=
self
.
headers
[
PyFunceble
.
STATUS
[
"official"
]
[
"down"
]
]
elif
self
.
template
.
lower
(
)
in
PyFunceble
.
STATUS
[
"list"
]
[
"invalid"
]
:
to_print
=
self
.
headers
[
PyFunceble
.
STATUS
[
"official"
]
[
"invalid"
]
]
elif
(
self
.
template
==
"Less"
or
self
.
template
==
"Percentage"
or
self
.
template
==
"HTTP"
)
:
to_print
=
self
.
headers
[
self
.
template
]
if
self
.
template
==
"Less"
and
not
PyFunceble
.
HTTP_CODE
[
"active"
]
:
to_print
[
"Source"
]
=
10
if
not
PyFunceble
.
HTTP_CODE
[
"active"
]
:
to_print
=
Dict
(
to_print
)
.
remove_key
(
"HTTP Code"
)
self
.
currently_used_header
=
to_print
if
not
do_not_print
:
self
.
_before_header
(
)
for
formatted_template
in
self
.
_header_constructor
(
to_print
)
:
if
not
self
.
only_on_file
:
print
(
formatted_template
)
if
not
PyFunceble
.
CONFIGURATION
[
"no_files"
]
and
self
.
output
:
File
(
self
.
output
)
.
write
(
formatted_template
+
"\n"
)

Management and creation of templates of header . Please consider as header the title of each columns .


def _data_constructor(self, size):
        """
        Construct the table of data according to given size.

        :param size: The maximal length of each string in the table.
        :type size: list

        :return:
            A dict with all information about the data and how to which what
            maximal size to print it.
        :rtype: OrderedDict

        :raises:
            :code:`Exception`
                If the data and the size does not have the same length.
        """

        # We initiate a variable which will save what we are going to
        # return.
        result = PyFunceble.OrderedDict()

        if len(self.data_to_print) == len(size):
            # The length of the data to print is equal to the length of the given size.

            for i in range(len(self.data_to_print)):
                # We loop until our iterator is less or equal to the length of the data
                # to print.

                # We initiate the result index and its size.
                result[self.data_to_print[i]] = size[i]
        else:
            # This should never happend. If it's happens then there is something
            # wrong from the inputed data.
            raise Exception(
                "Inputed: " + str(len(self.data_to_print)) + "; Size: " + str(len(size))
            )

        # We return the constructed result.
        return result

def
_data_constructor
(
self
,
size
)
:
result
=
PyFunceble
.
OrderedDict
(
)
if
len
(
self
.
data_to_print
)
==
len
(
size
)
:
for
i
in
range
(
len
(
self
.
data_to_print
)
)
:
result
[
self
.
data_to_print
[
i
]
]
=
size
[
i
]
else
:
raise
Exception
(
"Inputed: "
+
str
(
len
(
self
.
data_to_print
)
)
+
"; Size: "
+
str
(
len
(
size
)
)
)
return
result

Construct the table of data according to given size .


def _size_from_header(cls, header):
        """
        Get the size of each columns from the header.

        :param header:
            The header template we have to get the size from.
        :type header: dict

        :return: The maximal size of the each data to print.
        :rtype: list
        """

        # We initiate the result we are going to return.
        result = []

        for data in header:
            # We lopp through the header.

            # And we append the size to our result.
            result.append(header[data])

        # We return the result.
        return result

def _size_from_header ( cls , header ) : result = [ ] for data in header : result . append ( header [ data ] ) return result

Get the size of each columns from the header .


def _colorify(self, data):
        """
        Retun colored string.

        :param data: The string to colorify.
        :type data: str

        :return: A colored string.
        :rtype: str
        """

        if self.template in ["Generic", "Less"]:
            # The template is in the list of template that need the coloration.

            if (
                self.data_to_print[1].lower() in PyFunceble.STATUS["list"]["up"]
                or self.data_to_print[1].lower() in PyFunceble.STATUS["list"]["valid"]
            ):
                # The status is in the list of up status.

                # We print the data with a green background.
                data = PyFunceble.Fore.BLACK + PyFunceble.Back.GREEN + data
            elif self.data_to_print[1].lower() in PyFunceble.STATUS["list"]["down"]:
                # The status is in the list of down status.

                # We print the data with a red background.
                data = PyFunceble.Fore.BLACK + PyFunceble.Back.RED + data
            else:
                # The status is not in the list of up and down status.

                # We print the data with a cyan background.
                data = PyFunceble.Fore.BLACK + PyFunceble.Back.CYAN + data

        # We return the data.
        return data

def _colorify ( self , data ) : if self . template in [ "Generic" , "Less" ] : if ( self . data_to_print [ 1 ] . lower ( ) in PyFunceble . STATUS [ "list" ] [ "up" ] or self . data_to_print [ 1 ] . lower ( ) in PyFunceble . STATUS [ "list" ] [ "valid" ] ) : data = PyFunceble . Fore . BLACK + PyFunceble . Back . GREEN + data elif self . data_to_print [ 1 ] . lower ( ) in PyFunceble . STATUS [ "list" ] [ "down" ] : data = PyFunceble . Fore . BLACK + PyFunceble . Back . RED + data else : data = PyFunceble . Fore . BLACK + PyFunceble . Back . CYAN + data return data


def _json_print(self):  # pragma: no cover
        """
        Management of the json template.
        """

        if self.output:
            # The given output is not empty.

            if PyFunceble.path.isfile(self.output):
                # The given output already exist.

                # We get the content of the output.
                content = Dict().from_json(File(self.output).read())

                if isinstance(content, list):
                    # The content is a list.

                    # We extend the content with our data to print.
                    content.extend(self.data_to_print)

                    # We format our list.
                    content = List(content).custom_format(Sort.standard)

                    if PyFunceble.CONFIGURATION["hierarchical_sorting"]:
                        # The hierarchical sorting is activated.

                        # We format our content hierarchicaly
                        content = List(content).custom_format(Sort.hierarchical)

                    # We finally save our content into the file.
                    Dict(content).to_json(self.output)
                else:
                    # The content is not a list.

                    # We raise an exception.
                    raise Exception("Output not correctly formatted.")
            else:
                # The given output does not already exist.

                # We save our data to print into the output.
                #
                # Note: We do not have to take care if self.data_to_print is a list
                # formatted or not because this method should not be called if it is
                # not the case.
                Dict(self.data_to_print).to_json(self.output)
        else:
            # The given output is empty.

            # We raise an exception.
            raise Exception("Empty output given.")

def
_json_print
(
self
)
:
if
self
.
output
:
if
PyFunceble
.
path
.
isfile
(
self
.
output
)
:
content
=
Dict
(
)
.
from_json
(
File
(
self
.
output
)
.
read
(
)
)
if
isinstance
(
content
,
list
)
:
content
.
extend
(
self
.
data_to_print
)
content
=
List
(
content
)
.
custom_format
(
Sort
.
standard
)
if
PyFunceble
.
CONFIGURATION
[
"hierarchical_sorting"
]
:
content
=
List
(
content
)
.
custom_format
(
Sort
.
hierarchical
)
Dict
(
content
)
.
to_json
(
self
.
output
)
else
:
raise
Exception
(
"Output not correctly formatted."
)
else
:
Dict
(
self
.
data_to_print
)
.
to_json
(
self
.
output
)
else
:
raise
Exception
(
"Empty output given."
)


def data(self):  #  pragma: no cover  pylint: disable=inconsistent-return-statements
        """
        Management and input of data to the table.

        :raises:
            :code:`Exception`
                When self.data_to_print is not a list.
        """

        if isinstance(self.data_to_print, list):
            # The data to print is a list.

            # We initiate the data we are going to print.
            to_print = {}

            # We initiate the size we are going to print.
            to_print_size = []

            # We initiate a variable which will list the list of
            # alone case.
            alone_cases = ["Percentage", "HTTP"]

            # we initiate a variable which will list the list of
            # template which does not need a header.
            without_header = ["FullHosts", "PlainDomain"]

            if self.template.lower() == "json":
                # The template is the json template.

                if not PyFunceble.CONFIGURATION["no_files"] and self.output:
                    # * We are allowed to generate file.
                    # and
                    # * The given output is not empty.

                    # We print the json file.
                    return self._json_print()

                # We return nothing.
                return None

            if self.template not in alone_cases and self.template not in without_header:
                # * The template is not in the list of alone case.
                # and
                # * THe template is not in the list of template without header.

                # We get the template we should use.
                # Note: We basically only need the self.currently_used_header to be filled.
                self.header(True)

                # And we get the size from the header.
                to_print_size = self._size_from_header(self.currently_used_header)
            elif self.template in without_header:
                # The template is in the list of template which does not need a header.

                for data in self.data_to_print:
                    # We loop through the list of data to print.

                    # And we construct the (spacement) size of the data to print.
                    to_print_size.append(str(len(data)))
            else:
                # We get the size from the given template name.
                to_print_size = self._size_from_header(self.headers[self.template])

            # We construct and format the data to print.
            to_print = self._data_constructor(to_print_size)

            # We print the before header section.
            self._before_header()

            for data in self._header_constructor(to_print, False):
                # We loop through the formatted data.

                if self.template.lower() in PyFunceble.STATUS["list"][
                    "generic"
                ] or self.template in ["Less", "Percentage"]:
                    # * The template is in the list of generic status.
                    # or
                    # * The template is in a specific list.

                    if not self.only_on_file:
                        # We are authorized to print on screen.

                        # We colorify the data to print.
                        colorified_data = self._colorify(data)

                        # And we print the data.
                        print(colorified_data)
                if not PyFunceble.CONFIGURATION["no_files"] and self.output:
                    # * We are authorized to print on any file.
                    # and
                    # * The output is given.

                    # We write our data into the printed file.
                    File(self.output).write(data + "\n")
        else:
            # This should never happend. If it's happens then there's a big issue
            # around data_to_print.
            raise Exception("Please review Prints().data()")

def
data
(
self
)
:
if
isinstance
(
self
.
data_to_print
,
list
)
:
to_print
=
{
}
to_print_size
=
[
]
alone_cases
=
[
"Percentage"
,
"HTTP"
]
without_header
=
[
"FullHosts"
,
"PlainDomain"
]
if
self
.
template
.
lower
(
)
==
"json"
:
if
not
PyFunceble
.
CONFIGURATION
[
"no_files"
]
and
self
.
output
:
return
self
.
_json_print
(
)
return
if
self
.
template
not
in
alone_cases
and
self
.
template
not
in
without_header
:
self
.
header
(
True
)
to_print_size
=
self
.
_size_from_header
(
self
.
currently_used_header
)
elif
self
.
template
in
without_header
:
for
data
in
self
.
data_to_print
:
to_print_size
.
append
(
str
(
len
(
data
)
)
)
else
:
to_print_size
=
self
.
_size_from_header
(
self
.
headers
[
self
.
template
]
)
to_print
=
self
.
_data_constructor
(
to_print_size
)
self
.
_before_header
(
)
for
data
in
self
.
_header_constructor
(
to_print
,
False
)
:
if
self
.
template
.
lower
(
)
in
PyFunceble
.
STATUS
[
"list"
]
[
"generic"
]
or
self
.
template
in
[
"Less"
,
"Percentage"
]
:
if
not
self
.
only_on_file
:
colorified_data
=
self
.
_colorify
(
data
)
print
(
colorified_data
)
if
not
PyFunceble
.
CONFIGURATION
[
"no_files"
]
and
self
.
output
:
File
(
self
.
output
)
.
write
(
data
+
"\n"
)
else
:
raise
Exception
(
"Please review Prints().data()"
)

Management and input of data to the table .


def _save(self, last=False):  # pragma: no cover
        """
        Save the current time to the file.

        :param last:
            Tell us if we are at the very end of the file testing.
        :type last: bool
        """

        if (
            self._authorization()
            and PyFunceble.CONFIGURATION["logs"]
            and "file_to_test" in PyFunceble.INTERN
            and PyFunceble.INTERN["file_to_test"]
        ):
            # * We are authorized to work.
            # and
            # * The generation of logs is activated.
            # and
            # * We are not testing as an imported module.

            # We set the location of the file we are working with.
            self.file = (
                PyFunceble.OUTPUT_DIRECTORY
                + PyFunceble.OUTPUTS["parent_directory"]
                + PyFunceble.OUTPUTS["logs"]["directories"]["parent"]
                + PyFunceble.OUTPUTS["logs"]["filenames"]["execution_time"]
            )

            if PyFunceble.path.isfile(self.file):
                # The file we are working with exist.

                # We get its content so we can directly work with it.
                content = Dict().from_json(File(self.file).read())
            else:
                # The file we are working with does not exist.

                # We generate a dummy content.
                content = {}

            if self.action == "start":
                # The action is equal to `start`.

                if "final_total" in content and content["final_total"]:
                    # The final total index exist.

                    # We delete it.
                    del content["final_total"]

                if "data" in content:
                    # The data index exist.

                    # We append the current start time inside it at
                    # a new sublist.
                    content["data"].append([PyFunceble.INTERN["start"]])
                else:
                    # The data index does not exist.

                    # We create the index along with the current start time.
                    content["data"] = [[PyFunceble.INTERN["start"]]]
            elif self.action == "stop":
                # The action is equal to `stop`.

                try:
                    # We try to work with the data index.

                    # We append the end time at the end of the last element
                    # of data.
                    #
                    # Note: It is at the end because we should have as first
                    # the star time.
                    content["data"][-1].append(PyFunceble.INTERN["end"])

                    # We get the start time.
                    start = content["data"][0][0]
                    # We get the end time.
                    end = content["data"][-1][-1]

                    # We calculate the execution time of the test.
                    content["current_total"] = self.format_execution_time(start, end)

                    if last:
                        # We are at the very end of the file testing.

                        # We initiate the global execution time.
                        content["final_total"] = content["current_total"]

                        # We inform the user about the global execution time.
                        print(
                            PyFunceble.Fore.MAGENTA
                            + PyFunceble.Style.BRIGHT
                            + "Global execution time: "
                            + content["final_total"]
                        )
                except KeyError:
                    # It is not possible to work with the data index because
                    # it does not exist.

                    # We ignore the problem.
                    pass

            try:
                # We try to save the whole data at its final location.
                Dict(content).to_json(self.file)
            except FileNotFoundError:
                # The directory was not found.

                # We construct the output directory
                DirectoryStructure()

                # And we retry to save the whole data at its final location.
                Dict(content).to_json(self.file)

def
_save
(
self
,
last
=
False
)
:
if
(
self
.
_authorization
(
)
and
PyFunceble
.
CONFIGURATION
[
"logs"
]
and
"file_to_test"
in
PyFunceble
.
INTERN
and
PyFunceble
.
INTERN
[
"file_to_test"
]
)
:
self
.
file
=
(
PyFunceble
.
OUTPUT_DIRECTORY
+
PyFunceble
.
OUTPUTS
[
"parent_directory"
]
+
PyFunceble
.
OUTPUTS
[
"logs"
]
[
"directories"
]
[
"parent"
]
+
PyFunceble
.
OUTPUTS
[
"logs"
]
[
"filenames"
]
[
"execution_time"
]
)
if
PyFunceble
.
path
.
isfile
(
self
.
file
)
:
content
=
Dict
(
)
.
from_json
(
File
(
self
.
file
)
.
read
(
)
)
else
:
content
=
{
}
if
self
.
action
==
"start"
:
if
"final_total"
in
content
and
content
[
"final_total"
]
:
del
content
[
"final_total"
]
if
"data"
in
content
:
content
[
"data"
]
.
append
(
[
PyFunceble
.
INTERN
[
"start"
]
]
)
else
:
content
[
"data"
]
=
[
[
PyFunceble
.
INTERN
[
"start"
]
]
]
elif
self
.
action
==
"stop"
:
try
:
content
[
"data"
]
[
1
]
.
append
(
PyFunceble
.
INTERN
[
"end"
]
)
start
=
content
[
"data"
]
[
0
]
[
0
]
end
=
content
[
"data"
]
[
1
]
[
1
]
content
[
"current_total"
]
=
self
.
format_execution_time
(
start
,
end
)
if
last
:
content
[
"final_total"
]
=
content
[
"current_total"
]
print
(
PyFunceble
.
Fore
.
MAGENTA
+
PyFunceble
.
Style
.
BRIGHT
+
"Global execution time: "
+
content
[
"final_total"
]
)
except
KeyError
:
pass
try
:
Dict
(
content
)
.
to_json
(
self
.
file
)
except
FileNotFoundError
:
DirectoryStructure
(
)
Dict
(
content
)
.
to_json
(
self
.
file
)


def _calculate(cls, start=None, end=None):
        """
        calculate the difference between starting and ending time.

        :param start: A starting time.
        :type start: int|str

        :param stop: A ending time.
        :type stop: int|str

        :return:
            A dict with following as index.

                * :code:`days`
                * :code:`hours`
                * :code:`minutes`
                * :code:`seconds`

            as index.
        :rtype: dict
        """

        if start and end:
            # The start and end time is explicitly given.

            # We get the difference between the ending and the starting time.
            time_difference = int(end) - int(start)
        else:
            # The start and end time is not explicitly given.

            # We get the difference between the ending and the starting time.
            time_difference = PyFunceble.INTERN["end"] - PyFunceble.INTERN["start"]

        # We initiate an OrderedDict.
        # Indeed, we use an ordered dict because we want the structuration and the
        # order to stay always the same.
        # As a dictionnary is always unordered, we can use it. Otherwise the time will
        # not be shown correctly.
        data = PyFunceble.OrderedDict()

        # We calculate and append the day to our data.
        data["days"] = str(time_difference // (24 * 60 * 60)).zfill(2)

        # We calculate and append the hours to our data.
        data["hours"] = str((time_difference // (60 * 60)) % 24).zfill(2)

        # We calculate and append the minutes to our data.
        data["minutes"] = str((time_difference % 3600) // 60).zfill(2)

        # We calculate and append the minutes to our data.
        data["seconds"] = str(time_difference % 60).zfill(2)

        # We finaly return our data.
        return data

def _calculate ( cls , start = None , end = None ) : if start and end : time_difference = int ( end ) - int ( start ) else : time_difference = PyFunceble . INTERN [ "end" ] - PyFunceble . INTERN [ "start" ] data = PyFunceble . OrderedDict ( ) data [ "days" ] = str ( time_difference // ( 24 * 60 * 60 ) ) . zfill ( 2 ) data [ "hours" ] = str ( ( time_difference // ( 60 * 60 ) ) % 24 ) . zfill ( 2 ) data [ "minutes" ] = str ( ( time_difference % 3600 ) // 60 ) . zfill ( 2 ) data [ "seconds" ] = str ( time_difference % 60 ) . zfill ( 2 ) return data

calculate the difference between starting and ending time .


def format_execution_time(self, start=None, end=None):
        """
        Format the calculated time into a human readable format.

        :param start: A starting time.
        :type start: int|str

        :param stop: A ending time.
        :type stop: int|str

        :return: A human readable date.
        :rtype: str
        """

        # We return the formatted execution time.
        return ":".join(list(self._calculate(start, end).values()))

def format_execution_time ( self , start = None , end = None ) : return ":" . join ( list ( self . _calculate ( start , end ) . values ( ) ) )

Format the calculated time into a human readable format .


def file_to_delete(cls):
        """
        Return the list of file to delete.
        """

        # We initiate the directory we have to look for.
        directory = PyFunceble.OUTPUT_DIRECTORY + PyFunceble.OUTPUTS["parent_directory"]

        if not directory.endswith(PyFunceble.directory_separator):  # pragma: no cover
            # For safety, if it does not ends with the directory separator, we append it
            # to its end.
            directory += PyFunceble.directory_separator

        # We initiate a variable which will save the list of file to delete.
        result = []

        for root, _, files in PyFunceble.walk(directory):
            # We walk in the directory and get all files and sub-directories.

            for file in files:
                # If there is files in the current sub-directory, we loop
                # through the list of files.

                if file not in [".gitignore", ".keep"]:
                    # The file is not into our list of file we do not have to delete.

                    if root.endswith(PyFunceble.directory_separator):
                        # The root ends with the directory separator.

                        # We construct the path and append the full path to the result.
                        result.append(root + file)
                    else:
                        # The root directory does not ends with the directory separator.

                        # We construct the path by appending the directory separator
                        # between the root and the filename and append the full path to
                        # the result.
                        result.append(
                            root + PyFunceble.directory_separator + file
                        )  # pragma: no cover

        # We return our list of file to delete.
        return result

def file_to_delete ( cls ) : directory = PyFunceble . OUTPUT_DIRECTORY + PyFunceble . OUTPUTS [ "parent_directory" ] if not directory . endswith ( PyFunceble . directory_separator ) : directory += PyFunceble . directory_separator result = [ ] for root , _ , files in PyFunceble . walk ( directory ) : for file in files : if file not in [ ".gitignore" , ".keep" ] : if root . endswith ( PyFunceble . directory_separator ) : result . append ( root + file ) else : result . append ( root + PyFunceble . directory_separator + file ) return result


def databases_to_delete(cls):  # pragma: no cover
        """
        Set the databases files to delete.
        """

        # We initiate the directory we have to look for.
        directory = PyFunceble.CURRENT_DIRECTORY

        # We initate the result variable.
        result = []

        # We append the dir_structure file.
        result.append(
            directory
            + PyFunceble.CONFIGURATION["outputs"]["default_files"]["dir_structure"]
        )

        # We append the iana file.
        result.append(
            directory + PyFunceble.CONFIGURATION["outputs"]["default_files"]["iana"]
        )

        # We append the public suffix file.
        result.append(
            directory
            + PyFunceble.CONFIGURATION["outputs"]["default_files"]["public_suffix"]
        )

        # We append the inactive database file.
        result.append(
            directory
            + PyFunceble.CONFIGURATION["outputs"]["default_files"]["inactive_db"]
        )

        # We append the mining database file.
        result.append(
            directory + PyFunceble.CONFIGURATION["outputs"]["default_files"]["mining"]
        )

        # We append the whois database file.
        result.append(
            directory + PyFunceble.CONFIGURATION["outputs"]["default_files"]["whois_db"]
        )

        return result

def databases_to_delete ( cls ) : directory = PyFunceble . CURRENT_DIRECTORY result = [ ] result . append ( directory + PyFunceble . CONFIGURATION [ "outputs" ] [ "default_files" ] [ "dir_structure" ] ) result . append ( directory + PyFunceble . CONFIGURATION [ "outputs" ] [ "default_files" ] [ "iana" ] ) result . append ( directory + PyFunceble . CONFIGURATION [ "outputs" ] [ "default_files" ] [ "public_suffix" ] ) result . append ( directory + PyFunceble . CONFIGURATION [ "outputs" ] [ "default_files" ] [ "inactive_db" ] ) result . append ( directory + PyFunceble . CONFIGURATION [ "outputs" ] [ "default_files" ] [ "mining" ] ) result . append ( directory + PyFunceble . CONFIGURATION [ "outputs" ] [ "default_files" ] [ "whois_db" ] ) return result


def almost_everything(self, clean_all=False):
        """
        Delete almost all discovered files.

        :param clean_all:
            Tell the subsystem if we have to clean everything instesd
            of almost everything.
        :type clean_all: bool
        """

        # We get the list of file to delete.
        to_delete = self.file_to_delete()

        if clean_all:  # pragma: no cover
            to_delete.extend(self.databases_to_delete())

        for file in to_delete:
            # We loop through the list of file to delete.

            # And we delete the currently read file.
            File(file).delete()

        if clean_all:  # pragma: no cover
            Load(PyFunceble.CURRENT_DIRECTORY)

def almost_everything ( self , clean_all = False ) : to_delete = self . file_to_delete ( ) if clean_all : to_delete . extend ( self . databases_to_delete ( ) ) for file in to_delete : File ( file ) . delete ( ) if clean_all : Load ( PyFunceble . CURRENT_DIRECTORY )


def _hash_file(self, algo):
        """Get the hash of the given file

        :param algo: The algorithm to use.
        :type algo: str

        :return: The hexdigest of the data.
        :rtype: str
        """

        # We het the algorithm function.
        hash_data = getattr(hashlib, algo)()

        with open(self.path, "rb") as file:
            # We open an read the parsed path.

            # We read the content.
            content = file.read()

            # We parse the content to the hash algorithm.
            hash_data.update(content)

        # And we extract and return the hash.
        return hash_data.hexdigest()

def _hash_file ( self , algo ) : hash_data = getattr ( hashlib , algo ) ( ) with open ( self . path , "rb" ) as file : content = file . read ( ) hash_data . update ( content ) return hash_data . hexdigest ( )


def _hash_data(self, algo):
        """
        Get hash of the given data.

        :param algo: The algorithm to use.
        :type algo: str
        """

        # We het the algorithm function.
        hash_data = getattr(hashlib, algo)()

        # We set the data into our hashlib.
        hash_data.update(self.data)

        # And we extract and return the hash.
        return hash_data.hexdigest()

def _hash_data ( self , algo ) : hash_data = getattr ( hashlib , algo ) ( ) hash_data . update ( self . data ) return hash_data . hexdigest ( )


def get(self):
        """
        Return the hash of the given file
        """

        # We initiate a variable which will save the result we are going
        # to return.
        result = {}

        if self.algorithm in self.valid_algorithms:
            # * The parsed path exist.
            # and
            # * The parsed algorithm is in the list of valid algorithms.

            if self.algorithm == "all":
                # The parsed algorithm is `all`.

                # We remove `all` (the first element of the list) from
                # the list of valid algorithms because we are going to
                # loop through the list of valid algorithms.
                del self.valid_algorithms[0]

                for algo in self.valid_algorithms:
                    # We loop through the list of valid algorithms.

                    if self.path and path.isfile(self.path):
                        # The file path exist.

                        # We save the hash into the result variable.
                        result[algo] = self._hash_file(algo)
                    elif self.data:
                        # * The path does not exist.
                        # and
                        # * The given data is not empty.

                        # We save the hash into the result variable.
                        result[algo] = self._hash_data(algo)
                    else:  # pragma: no cover
                        # All other case are met.

                        # We return None.
                        return None
            else:
                # The parsed algorithm is a specific one.

                if self.path and path.isfile(self.path):
                    # The file path exist.

                    # We save the hash into the result variable.
                    result[self.algorithm] = self._hash_file(self.algorithm)
                elif self.data:
                    # * The path does not exist.
                    # and
                    # * The given data is not empty.

                    # We save the hash into the result variable.
                    result[self.algorithm] = self._hash_data(self.algorithm)
                else:
                    # All the other case are met.

                    # We return None.
                    return None
        else:  # pragma: no cover
            # The parsed algorithm is not in the list of valid algorithms.
            return None

        if self.algorithm != "all" and self.only_hash:
            # * The parsed algorithm is not equal to `all`.
            # and
            # * We only have to return the selected hash.

            # We return the selected algorithm.
            return result[self.algorithm]

        # * The parsed algorithm is equal to `all`.
        # or
        # * We do not have to return the selected hash.

        # We return all hashes.
        return result

def get ( self ) : result = { } if self . algorithm in self . valid_algorithms : if self . algorithm == "all" : del self . valid_algorithms [ 0 ] for algo in self . valid_algorithms : if self . path and path . isfile ( self . path ) : result [ algo ] = self . _hash_file ( algo ) elif self . data : result [ algo ] = self . _hash_data ( algo ) else : return None else : if self . path and path . isfile ( self . path ) : result [ self . algorithm ] = self . _hash_file ( self . algorithm ) elif self . data : result [ self . algorithm ] = self . _hash_data ( self . algorithm ) else : return None else : return None if self . algorithm != "all" and self . only_hash : return result [ self . algorithm ] return result


def execute(self):
        """
        Execute the given command.

        :return: The output of the command.
        :rtype: str
        """

        # We initiate a process and parse the command to it.
        process = Popen(self.command, stdout=PIPE, stderr=PIPE, shell=True)

        # We communicate the command and get the output and the error.
        (output, error) = process.communicate()

        if process.returncode != 0:  # pragma: no cover
            # The return code is different to 0.

            # We return the decoded error.
            return self._decode_output(error)

        # The return code (or exit code if you prefer) if equal to 0.

        # We return the decoded output of the executed command.
        return self._decode_output(output)

def execute ( self ) : process = Popen ( self . command , stdout = PIPE , stderr = PIPE , shell = True ) ( output , error ) = process . communicate ( ) if process . returncode != 0 : return self . _decode_output ( error ) return self . _decode_output ( output )


def remove_key(self, key_to_remove):
        """
        Remove a given key from a given dictionary.

        :param key_to_remove: The key(s) to delete.
        :type key_to_remove: list|str

        :return: The dict without the given key(s).
        :rtype: dict|None
        """

        if isinstance(self.main_dictionnary, dict):
            # The main dictionnary is a dictionnary

            if isinstance(key_to_remove, list):
                # The parsed key to remove is a list.

                for key in key_to_remove:
                    # We loop through the list of key to remove.

                    # We delete the key from the dictionnary.
                    del self.main_dictionnary[key]
            else:
                # The parsed key to remove is not a list.

                try:
                    # We delete the given key from the dictionnary.
                    del self.main_dictionnary[key_to_remove]
                except KeyError:
                    pass

            # We return the final dictionnary.
            return self.main_dictionnary

        # The main dictionnary is not a dictionnary.

        # We return None.
        return None

def remove_key ( self , key_to_remove ) : if isinstance ( self . main_dictionnary , dict ) : if isinstance ( key_to_remove , list ) : for key in key_to_remove : del self . main_dictionnary [ key ] else : try : del self . main_dictionnary [ key_to_remove ] except KeyError : pass return self . main_dictionnary return None

Remove a given key from a given dictionary .


def rename_key(self, key_to_rename, strict=True):
        """
        Rename the given keys from the given dictionary.

        :param key_to_rename:
            The key(s) to rename.
            Expected format: :code:`{old:new}`
        :type key_to_rename: dict

        :param strict:
            Tell us if we have to rename the exact index or
            the index which looks like the given key(s)

        :return: The well formatted dict.
        :rtype: dict|None
        """

        if isinstance(self.main_dictionnary, dict) and isinstance(key_to_rename, dict):
            # * The given main directory is a dictionnary.
            # and
            # * The given key to rename is a dictionnary.

            for old, new in key_to_rename.items():
                # We loop through the key to raname.

                if strict:
                    # The strict method is activated.
                    if old in self.main_dictionnary:
                        # The old key is in the main dictionnary.

                        # We initiate the new with the old and remove the old content.
                        self.main_dictionnary[new] = self.main_dictionnary.pop(old)
                else:
                    # The strict method is not activated.

                    # We initiate the elements to rename.
                    to_rename = {}

                    for index in self.main_dictionnary:
                        # We loop throught the indexes of the main dictionnary.

                        if old in index:
                            # The old key is into the index name.

                            # We append the index name and the new index to our
                            # local list to rename.
                            to_rename.update({index: new[:-1] + index.split(old)[-1]})

                    # We run this method against the local list to rename in order
                    # to rename the element.
                    self.main_dictionnary = Dict(self.main_dictionnary).rename_key(
                        to_rename, True
                    )

            # We return the final list.
            return self.main_dictionnary

        # * The given main directory is not a dictionnary.
        # or
        # * The given key to rename is not a dictionnary.

        # We return None.
        return None

def rename_key ( self , key_to_rename , strict = True ) : if isinstance ( self . main_dictionnary , dict ) and isinstance ( key_to_rename , dict ) : for old , new in key_to_rename . items ( ) : if strict : if old in self . main_dictionnary : self . main_dictionnary [ new ] = self . main_dictionnary . pop ( old ) else : to_rename = { } for index in self . main_dictionnary : if old in index : to_rename . update ( { index : new [ : - 1 ] + index . split ( old ) [ - 1 ] } ) self . main_dictionnary = Dict ( self . main_dictionnary ) . rename_key ( to_rename , True ) return self . main_dictionnary return None

Rename the given keys from the given dictionary .


def merge(self, to_merge, strict=True):
        """
        Merge the content of to_merge into the given main dictionnary.

        :param to_merge: The dictionnary to merge.
        :type to_merge: dict

        :param strict:
            Tell us if we have to strictly merge lists.

            :code:`True`: We follow index
            :code`False`: We follow element (content)
        :type strict: bool

        :return: The merged dict.
        :rtype: dict
        """

        # We initiate a variable which will save our result.
        result = {}

        for element in to_merge:
            # We loop throught the given dict to merge.

            if element in self.main_dictionnary:
                # The currently read element is in the main dict.

                if isinstance(to_merge[element], dict) and isinstance(
                    self.main_dictionnary[element], dict
                ):
                    # They are in both side dict.

                    # We merge the dict tree and save into result.
                    result[element] = Dict(self.main_dictionnary[element]).merge(
                        to_merge[element]
                    )

                elif isinstance(to_merge[element], list) and isinstance(
                    self.main_dictionnary[element], list
                ):
                    # They are in both side list.

                    # We merge the lists and save into result.
                    result[element] = List(self.main_dictionnary[element]).merge(
                        to_merge[element], strict
                    )
                else:
                    # They are not list, not dict.

                    # We append the currently read element to the result.
                    result.update({element: to_merge[element]})
            else:
                # The currently read element is not into the main
                # dict.

                # We append the currently read element to the result.
                result.update({element: to_merge[element]})

        for element in self.main_dictionnary:
            # We loop through each element of the main dict.

            if element not in result:
                # The currently read element is not into
                # the result.

                # We append it to the result.
                result[element] = self.main_dictionnary[element]

        # We return the result.
        return result

def merge ( self , to_merge , strict = True ) : result = { } for element in to_merge : if element in self . main_dictionnary : if isinstance ( to_merge [ element ] , dict ) and isinstance ( self . main_dictionnary [ element ] , dict ) : result [ element ] = Dict ( self . main_dictionnary [ element ] ) . merge ( to_merge [ element ] ) elif isinstance ( to_merge [ element ] , list ) and isinstance ( self . main_dictionnary [ element ] , list ) : result [ element ] = List ( self . main_dictionnary [ element ] ) . merge ( to_merge [ element ] , strict ) else : result . update ( { element : to_merge [ element ] } ) else : result . update ( { element : to_merge [ element ] } ) for element in self . main_dictionnary : if element not in result : result [ element ] = self . main_dictionnary [ element ] return result

Merge the content of to_merge into the given main dictionnary .


def to_json(self, destination):
        """
        Save a dictionnary into a JSON file.

        :param destination:
            A path to a file where we're going to
            write the converted dict into a JSON format.
        :type destination: str
        """

        try:
            with open(destination, "w") as file:
                # We open the file we are going to write.
                # Note: We always overwrite the destination.

                # We save the current dictionnary into a json format.
                dump(
                    self.main_dictionnary,
                    file,
                    ensure_ascii=False,
                    indent=4,
                    sort_keys=True,
                )
        except UnicodeEncodeError:  # pragma: no cover
            with open(destination, "w", encoding="utf-8") as file:
                # We open the file we are going to write.
                # Note: We always overwrite the destination.

                # We save the current dictionnary into a json format.
                dump(
                    self.main_dictionnary,
                    file,
                    ensure_ascii=False,
                    indent=4,
                    sort_keys=True,
                )

def to_json ( self , destination ) : try : with open ( destination , "w" ) as file : dump ( self . main_dictionnary , file , ensure_ascii = False , indent = 4 , sort_keys = True , ) except UnicodeEncodeError : with open ( destination , "w" , encoding = "utf-8" ) as file : dump ( self . main_dictionnary , file , ensure_ascii = False , indent = 4 , sort_keys = True , )


def to_yaml(self, destination, flow_style=False):
        """
        Save a dictionnary into a YAML file.

        :param destination:
            A path to a file where we're going to write the
            converted dict into a JSON format.
        :type destination: str
        """

        with open(destination, "w") as file:
            # We open the file we are going to write.
            # Note: We always overwrite the destination.

            # We save the current dictionnary into a json format.
            dump_yaml(
                self.main_dictionnary,
                file,
                encoding="utf-8",
                allow_unicode=True,
                indent=4,
                default_flow_style=flow_style,
            )

def to_yaml ( self , destination , flow_style = False ) : with open ( destination , "w" ) as file : dump_yaml ( self . main_dictionnary , file , encoding = "utf-8" , allow_unicode = True , indent = 4 , default_flow_style = flow_style , )


def fix_path(self, splited_path=None):
        """
        Fix the path of the given path.

        :param splited_path: A list to convert to the right path.
        :type splited_path: list

        :return: The fixed path.
        :rtype: str
        """

        if not splited_path:
            # A splited path is parsed.

            # We initate a variable which will save the splited path.
            split_path = []

            if self.directory:
                # The parsed directory is not empty or equal to None.

                if "/" in self.directory:
                    # We split the separator.
                    split_path = self.directory.split("/")
                elif "\\" in self.directory:
                    # We split the separator.
                    split_path = self.directory.split("\\")
                else:
                    split_path = [self.directory]

                # We run the same function with the splited_path argument filled.
                return self.fix_path(splited_path=[x for x in split_path if x])

            # We return the directory.
            return self.directory

        # We join the splited element with the directory separator as glue.
        return directory_separator.join(splited_path) + directory_separator

def fix_path ( self , splited_path = None ) : if not splited_path : split_path = [ ] if self . directory : if "/" in self . directory : split_path = self . directory . split ( "/" ) elif "\\" in self . directory : split_path = self . directory . split ( "\\" ) else : split_path = [ self . directory ] return self . fix_path ( splited_path = [ x for x in split_path if x ] ) return self . directory return directory_separator . join ( splited_path ) + directory_separator


def write(self, data_to_write, overwrite=False):
        """
        Write or append data into the given file path.

        :param data_to_write: The data to write.
        :type data_to_write: str

        :param overwrite:
            Tell us if we have to overwrite the
            content of the file we are working with.
        :type overwrite: bool
        """

        if overwrite or not path.isfile(self.file):
            # * We have to overwrite the file data.
            # or
            # * The file path does not already exist.

            with open(self.file, "w", encoding="utf-8", newline="\n") as file:
                # We prepare the file for writting.

                if data_to_write and isinstance(data_to_write, str):
                    # * A data  to write is given.
                    # and
                    # * The data to write is a string

                    # We write the string into the file.
                    file.write(data_to_write)
        else:
            # * We do not have to overwrite the file data.
            # or
            # * The file path does already exist.

            with open(self.file, "a", encoding="utf-8", newline="\n") as file:
                # We prepare the file for append writting.

                if data_to_write and isinstance(data_to_write, str):
                    # * A data  to write is given.
                    # and
                    # * The data to write is a string

                    # We append the string into the file.
                    file.write(data_to_write)

def write ( self , data_to_write , overwrite = False ) : if overwrite or not path . isfile ( self . file ) : with open ( self . file , "w" , encoding = "utf-8" , newline = "\n" ) as file : if data_to_write and isinstance ( data_to_write , str ) : file . write ( data_to_write ) else : with open ( self . file , "a" , encoding = "utf-8" , newline = "\n" ) as file : if data_to_write and isinstance ( data_to_write , str ) : file . write ( data_to_write )

Write or append data into the given file path .


def read(self):
        """
        Read a given file path and return its content.

        :return: The content of the given file path.
        :rtype: str
        """

        try:
            with open(self.file, "r", encoding="utf-8") as file:
                # We open and read a file.

                # We get the file content.
                funilrys = file.read()
        except UnicodeDecodeError:  # pragma: no cover
            with open(self.file, "r") as file:
                # We open and read a file.

                # We get the file content.
                funilrys = file.read()

        # We return the file content.
        return funilrys

def read ( self ) : try : with open ( self . file , "r" , encoding = "utf-8" ) as file : funilrys = file . read ( ) except UnicodeDecodeError : with open ( self . file , "r" ) as file : funilrys = file . read ( ) return funilrys

Read a given file path and return its content .


def format(self):
        """
        Return a well formatted list. Basicaly, it's sort a list and remove duplicate.

        :return: A sorted, without duplicate, list.
        :rtype: list
        """

        try:
            return sorted(list(set(self.main_list)), key=str.lower)

        except TypeError:  # pragma: no cover
            return self.main_list

def format ( self ) : try : return sorted ( list ( set ( self . main_list ) ) , key = str . lower ) except TypeError : return self . main_list

Return a well formatted list . Basicaly it s sort a list and remove duplicate .


def merge(self, to_merge, strict=True):
        """
        Merge to_merge into the given main list.

        :param to_merge: The list to merge.
        :type to_merge: list

        :param strict:
            Tell us if we have to respect index (True)
            or not (False).
        :type strict: bool

        :return: The merged list.
        :rtype: list
        """

        # We initiate a variable which will save the
        # result
        result = []

        if strict:
            # We are in strict mode.

            for index, element in enumerate(to_merge):
                # We loop through each element of the list to merge
                # to the main dict.

                try:
                    if isinstance(element, dict) and isinstance(
                        self.main_list[index], dict
                    ):
                        # The currently read element is a dict.

                        # We merge its content into the main dict
                        # and append into the result.
                        result.append(Dict(self.main_list[index]).merge(element))
                    elif isinstance(element, list) and isinstance(
                        self.main_list[index], list
                    ):
                        # The currently read element is a list.

                        # We loop through this method.
                        result.append(List(self.main_list[index]).merge(element))
                    else:
                        # The currently read element is not a list
                        # nor a dict.

                        # We append the element to the result.
                        result.append(element)
                except IndexError:  # pragma: no cover
                    # The index does not exist.
                    # Which means that for example one list is bigger
                    # than the other one.

                    # We append the element to the result.
                    result.append(element)
        else:
            # We are not is strict mode.

            # We initiate the result with the main list.
            result = self.main_list

            for element in to_merge:
                # We loop through the element to merge.

                if element not in result:
                    # The currently read element is not
                    # in the result.

                    # We append it to the result
                    result.append(element)

        # We return the result.
        return result

def merge ( self , to_merge , strict = True ) : result = [ ] if strict : for index , element in enumerate ( to_merge ) : try : if isinstance ( element , dict ) and isinstance ( self . main_list [ index ] , dict ) : result . append ( Dict ( self . main_list [ index ] ) . merge ( element ) ) elif isinstance ( element , list ) and isinstance ( self . main_list [ index ] , list ) : result . append ( List ( self . main_list [ index ] ) . merge ( element ) ) else : result . append ( element ) except IndexError : result . append ( element ) else : result = self . main_list for element in to_merge : if element not in result : result . append ( element ) return result

Merge to_merge into the given main list .


def not_matching_list(self):
        """
        Return a list of string which don't match the
        given regex.
        """

        pre_result = comp(self.regex)

        return [x for x in self.data if not pre_result.search(str(x))]

def not_matching_list ( self ) : pre_result = comp ( self . regex ) return [ x for x in self . data if not pre_result . search ( str ( x ) ) ]

Return a list of string which don t match the given regex .


def match(self):
        """
        Used to get exploitable result of re.search

        :return: The data of the match status.
        :rtype: mixed
        """

        # We initate this variable which gonna contain the returned data
        result = []

        # We compile the regex string
        to_match = comp(self.regex)

        # In case we have to use the implementation of ${BASH_REMATCH} we use
        # re.findall otherwise, we use re.search
        if self.rematch:  # pylint: disable=no-member
            pre_result = to_match.findall(self.data)
        else:
            pre_result = to_match.search(self.data)

        if self.return_data and pre_result:  # pylint: disable=no-member
            if self.rematch:  # pylint: disable=no-member
                for data in pre_result:
                    if isinstance(data, tuple):
                        result.extend(list(data))
                    else:
                        result.append(data)

                if self.group != 0:  # pylint: disable=no-member
                    return result[self.group]  # pylint: disable=no-member

            else:
                result = pre_result.group(
                    self.group  # pylint: disable=no-member
                ).strip()

            return result

        if not self.return_data and pre_result:  # pylint: disable=no-member
            return True

        return False

def match ( self ) : result = [ ] to_match = comp ( self . regex ) if self . rematch : pre_result = to_match . findall ( self . data ) else : pre_result = to_match . search ( self . data ) if self . return_data and pre_result : if self . rematch : for data in pre_result : if isinstance ( data , tuple ) : result . extend ( list ( data ) ) else : result . append ( data ) if self . group != 0 : return result [ self . group ] else : result = pre_result . group ( self . group ) . strip ( ) return result if not self . return_data and pre_result : return True return False

Used to get exploitable result of re . search


def replace(self):
        """
        Used to replace a matched string with another.

        :return: The data after replacement.
        :rtype: str
        """

        if self.replace_with:  # pylint: disable=no-member
            return substrings(
                self.regex,
                self.replace_with,  # pylint: disable=no-member
                self.data,
                self.occurences,  # pylint: disable=no-member
            )

        return self.data

def replace ( self ) : if self . replace_with : return substrings ( self . regex , self . replace_with , self . data , self . occurences , ) return self . data

Used to replace a matched string with another .


def count(self):
        """
        Count the number of domain for each status.
        """

        if self.status:
            # The status is parsed.

            # We increase the number of tested.
            PyFunceble.INTERN["counter"]["number"]["tested"] += 1

            if (
                self.status.lower() in PyFunceble.STATUS["list"]["up"]
                or self.status.lower() in PyFunceble.STATUS["list"]["valid"]
            ):
                # The status is in the list of up status.

                # We increase the number of up.
                PyFunceble.INTERN["counter"]["number"]["up"] += 1
            elif self.status.lower() in PyFunceble.STATUS["list"]["down"]:
                # The status is in the list of down status.

                # We increase the number of down.
                PyFunceble.INTERN["counter"]["number"]["down"] += 1
            else:
                # The status is not in the list of up nor down status.

                # We increase the number of invalid.
                PyFunceble.INTERN["counter"]["number"]["invalid"] += 1

def count ( self ) : if self . status : PyFunceble . INTERN [ "counter" ] [ "number" ] [ "tested" ] += 1 if ( self . status . lower ( ) in PyFunceble . STATUS [ "list" ] [ "up" ] or self . status . lower ( ) in PyFunceble . STATUS [ "list" ] [ "valid" ] ) : PyFunceble . INTERN [ "counter" ] [ "number" ] [ "up" ] += 1 elif self . status . lower ( ) in PyFunceble . STATUS [ "list" ] [ "down" ] : PyFunceble . INTERN [ "counter" ] [ "number" ] [ "down" ] += 1 else : PyFunceble . INTERN [ "counter" ] [ "number" ] [ "invalid" ] += 1

Count the number of domain for each status .


def _calculate(cls):
        """
        Calculate the percentage of each status.
        """

        # We map the current state/counters of the different status.
        percentages = {
            "up": PyFunceble.INTERN["counter"]["number"]["up"],
            "down": PyFunceble.INTERN["counter"]["number"]["down"],
            "invalid": PyFunceble.INTERN["counter"]["number"]["invalid"],
        }

        for percentage in percentages:
            # We loop through our map index.

            # We calculate the percentage.
            calculation = (
                percentages[percentage]
                * 100
                // PyFunceble.INTERN["counter"]["number"]["tested"]
            )

            # And we update the percentage counter of the actual status.
            PyFunceble.INTERN["counter"]["percentage"].update({percentage: calculation})

def _calculate ( cls ) : percentages = { "up" : PyFunceble . INTERN [ "counter" ] [ "number" ] [ "up" ] , "down" : PyFunceble . INTERN [ "counter" ] [ "number" ] [ "down" ] , "invalid" : PyFunceble . INTERN [ "counter" ] [ "number" ] [ "invalid" ] , } for percentage in percentages : calculation = ( percentages [ percentage ] * 100 // PyFunceble . INTERN [ "counter" ] [ "number" ] [ "tested" ] ) PyFunceble . INTERN [ "counter" ] [ "percentage" ] . update ( { percentage : calculation } )

Calculate the percentage of each status .


def log(self):
        """
        Print on screen and on file the percentages for each status.
        """

        if (
            PyFunceble.CONFIGURATION["show_percentage"]
            and PyFunceble.INTERN["counter"]["number"]["tested"] > 0
        ):
            # * We are allowed to show the percentage on screen.
            # and
            # * The number of tested is greater than 0.

            # We initiate the output file.
            output = (
                PyFunceble.OUTPUT_DIRECTORY
                + PyFunceble.OUTPUTS["parent_directory"]
                + PyFunceble.OUTPUTS["logs"]["directories"]["parent"]
                + PyFunceble.OUTPUTS["logs"]["directories"]["percentage"]
                + PyFunceble.OUTPUTS["logs"]["filenames"]["percentage"]
            )

            # We delete the output file if it does exist.
            File(output).delete()

            # We calculate the percentage of each statuses.
            self._calculate()

            if not PyFunceble.CONFIGURATION["quiet"]:
                # The quiet mode is activated.

                # We print a new line.
                print("\n")

                # We print the percentage header on file and screen.
                Prints(None, "Percentage", output).header()

                # We construct the different lines/data to print on screen and file.
                lines_to_print = [
                    [
                        PyFunceble.STATUS["official"]["up"],
                        str(PyFunceble.INTERN["counter"]["percentage"]["up"]) + "%",
                        PyFunceble.INTERN["counter"]["number"]["up"],
                    ],
                    [
                        PyFunceble.STATUS["official"]["down"],
                        str(PyFunceble.INTERN["counter"]["percentage"]["down"]) + "%",
                        PyFunceble.INTERN["counter"]["number"]["down"],
                    ],
                    [
                        PyFunceble.STATUS["official"]["invalid"],
                        str(PyFunceble.INTERN["counter"]["percentage"]["invalid"])
                        + "%",
                        PyFunceble.INTERN["counter"]["number"]["invalid"],
                    ],
                ]

                if PyFunceble.CONFIGURATION["syntax"]:
                    # We are checking for syntax.

                    # We update the denomination of the UP.
                    lines_to_print[0][0] = PyFunceble.STATUS["official"]["valid"]

                    # And we unset the INACTIVE line.
                    del lines_to_print[1]

                for to_print in lines_to_print:
                    # We loop throught the different line to print.
                    # (one line for each status.)

                    # And we print the current status line on file and screen.
                    Prints(to_print, "Percentage", output).data()

        elif PyFunceble.INTERN["counter"]["number"]["tested"] > 0:
            # * We are not allowed to show the percentage on screen.
            # but
            # * The number of tested is greater than 0.

            # We run the calculation.
            # Note: The following is needed, because all counter calculation are
            # done by this class.
            self._calculate()

def log ( self ) : if ( PyFunceble . CONFIGURATION [ "show_percentage" ] and PyFunceble . INTERN [ "counter" ] [ "number" ] [ "tested" ] > 0 ) : output = ( PyFunceble . OUTPUT_DIRECTORY + PyFunceble . OUTPUTS [ "parent_directory" ] + PyFunceble . OUTPUTS [ "logs" ] [ "directories" ] [ "parent" ] + PyFunceble . OUTPUTS [ "logs" ] [ "directories" ] [ "percentage" ] + PyFunceble . OUTPUTS [ "logs" ] [ "filenames" ] [ "percentage" ] ) File ( output ) . delete ( ) self . _calculate ( ) if not PyFunceble . CONFIGURATION [ "quiet" ] : print ( "\n" ) Prints ( None , "Percentage" , output ) . header ( ) lines_to_print = [ [ PyFunceble . STATUS [ "official" ] [ "up" ] , str ( PyFunceble . INTERN [ "counter" ] [ "percentage" ] [ "up" ] ) + "%" , PyFunceble . INTERN [ "counter" ] [ "number" ] [ "up" ] , ] , [ PyFunceble . STATUS [ "official" ] [ "down" ] , str ( PyFunceble . INTERN [ "counter" ] [ "percentage" ] [ "down" ] ) + "%" , PyFunceble . INTERN [ "counter" ] [ "number" ] [ "down" ] , ] , [ PyFunceble . STATUS [ "official" ] [ "invalid" ] , str ( PyFunceble . INTERN [ "counter" ] [ "percentage" ] [ "invalid" ] ) + "%" , PyFunceble . INTERN [ "counter" ] [ "number" ] [ "invalid" ] , ] , ] if PyFunceble . CONFIGURATION [ "syntax" ] : lines_to_print [ 0 ] [ 0 ] = PyFunceble . STATUS [ "official" ] [ "valid" ] del lines_to_print [ 1 ] for to_print in lines_to_print : Prints ( to_print , "Percentage" , output ) . data ( ) elif PyFunceble . INTERN [ "counter" ] [ "number" ] [ "tested" ] > 0 : self . _calculate ( )

Print on screen and on file the percentages for each status .


def is_url_valid(self, url=None, return_base=False, return_formatted=False):
        """
        Check if the given URL is valid.

        :param url: The url to validate.
        :type url: str

        :param return_base:
            Allow us the return of the url base (if URL formatted correctly).
        :type return_formatted: bool

        :param return_formatted:
            Allow us to get the URL converted to IDNA if the conversion
            is activated.
        :type return_formatted: bool

        :return: The validity of the URL or its base.
        :rtype: bool|str
        """

        # We initiate a variable which will save the initial base in case
        # we have to convert the base to IDNA.
        initial_base = None

        if url:
            # The given url is not empty.

            # We initiate the element to test.
            to_test = url
        elif self.element:
            # The globaly given url is not empty.

            # We initiate the element to test.
            to_test = self.element
        else:
            # The given url is empty.

            # We initiate the element to test from the globaly URl to test.
            to_test = PyFunceble.INTERN["to_test"]

        if to_test.startswith("http"):
            # The element to test starts with http.

            try:
                # We initiate a regex which will match the domain or the url base.
                regex = r"(^(http:\/\/|https:\/\/)(.+?(?=\/)|.+?$))"

                # We extract the url base with the help of the initiated regex.
                initial_base = base = Regex(
                    to_test, regex, return_data=True, rematch=True
                ).match()[2]

                if PyFunceble.CONFIGURATION["idna_conversion"]:
                    # We have to convert the domain to IDNA.

                    # We convert the initial base to IDNA.
                    base = domain2idna(base)

                # We check if the url base is a valid domain.
                domain_status = self.is_domain_valid(base)

                # We check if the url base is a valid IP.
                ip_status = self.is_ip_valid(base)

                if domain_status or ip_status:
                    # * The url base is a valid domain.
                    # and
                    # * The url base is a valid IP.

                    if PyFunceble.CONFIGURATION["idna_conversion"] and return_formatted:
                        # * We have to convert to IDNA.
                        # and
                        # * We have to return the converted full URL.

                        # We return the converted full URL.
                        return Regex(
                            to_test,
                            initial_base,
                            escape=True,
                            return_data=True,
                            replace_with=base,
                            occurences=1,
                        ).replace()

                    if return_formatted:
                        # * We do not have to convert to IDNA.
                        # but
                        # * We have to return the full URL.

                        # We return the initially given URL.
                        return to_test

                    if return_base:
                        # We have to return the base of the URL.

                        # We return the base of the URL.
                        return base

                    # We return True.
                    return True
            except TypeError:
                pass

        if return_formatted:
            # We have to return an URL.

            # We return the initily given URL.
            return to_test

        # We return False.
        return False


def is_domain_valid(
        self, domain=None, subdomain_check=False
    ):  # pylint:disable=too-many-return-statements, too-many-branches
        """
        Check if the given domain is a valid.

        :param domain: The domain to validate.
        :type domain: str

        :param subdomain_check:
            Activate the subdomain checking.
        :type subdomain_check: bool

        :return: The validity of the sub-domain.
        :rtype: bool
        """

        # We initate our regex which will match for valid domains.
        regex_valid_domains = r"^(?=.{0,253}$)(([a-z0-9][a-z0-9-]{0,61}[a-z0-9]|[a-z0-9])\.)+((?=.*[^0-9])([a-z0-9][a-z0-9-]{0,61}[a-z0-9](?:\.)?|[a-z0-9](?:\.)?))$"  # pylint: disable=line-too-long

        # We initiate our regex which will match for valid subdomains.
        regex_valid_subdomains = r"^(?=.{0,253}$)(([a-z0-9_][a-z0-9-_]{0,61}[a-z0-9_-]|[a-z0-9])\.)+((?=.*[^0-9])([a-z0-9][a-z0-9-]{0,61}[a-z0-9]|[a-z0-9]))$"  # pylint: disable=line-too-long

        if domain:
            # A domain is given.

            # We set the element to test as the parsed domain.
            to_test = domain
        elif self.element:
            # A domain is globally given.

            # We set the globally parsed domain.
            to_test = self.element
        else:
            # A domain is not given.

            # We set the element to test as the currently tested element.
            to_test = PyFunceble.INTERN["to_test"]

        try:
            # We get the position of the last point.
            last_point_index = to_test.rindex(".")
            # And with the help of the position of the last point, we get the domain extension.
            extension = to_test[last_point_index + 1 :]

            if not extension and to_test.endswith("."):
                try:
                    extension = [x for x in to_test.split(".") if x][-1]
                except IndexError:
                    pass

            if not extension or extension not in PyFunceble.INTERN["iana_db"]:
                # * The extension is not found.
                # or
                # * The extension is not into the IANA database.

                # We return false.
                return False

            if (
                Regex(to_test, regex_valid_domains, return_data=False).match()
                and not subdomain_check
            ):
                # * The element pass the domain validation.
                # and
                # * We are not checking if it is a subdomain.

                # We return True. The domain is valid.
                return True

            # The element did not pass the domain validation. That means that
            # it has invalid character or the position of - or _ are not right.

            if extension in PyFunceble.INTERN["psl_db"]:
                # The extension is into the psl database.

                for suffix in PyFunceble.INTERN["psl_db"][extension]:
                    # We loop through the element of the extension into the psl database.

                    try:
                        # We try to get the position of the currently read suffix
                        # in the element ot test.
                        suffix_index = to_test.rindex("." + suffix)

                        # We get the element to check.
                        # The idea here is to delete the suffix, then retest with our
                        # subdomains regex.
                        to_check = to_test[:suffix_index]

                        if "." not in to_check and subdomain_check:
                            # * There is no point into the new element to check.
                            # and
                            # * We are checking if it is a subdomain.

                            # We return False, it is not a subdomain.
                            return False

                        if "." in to_check and subdomain_check:
                            # * There is a point into the new element to check.
                            # and
                            # * We are checking if it is a subdomain.

                            # We return True, it is a subdomain.
                            return True

                        # We are not checking if it is a subdomain.

                        if "." in to_check:
                            # There is a point into the new element to check.

                            # We check if it passes our subdomain regex.
                            # * True: It's a valid domain.
                            # * False: It's an invalid domain.
                            return Regex(
                                to_check, regex_valid_subdomains, return_data=False
                            ).match()

                    except ValueError:
                        # In case of a value error because the position is not found,
                        # we continue to the next element.
                        pass

            # * The extension is not into the psl database.
            # or
            # * there was no point into the suffix checking.

            # We get the element before the last point.
            to_check = to_test[:last_point_index]

            if "." in to_check and subdomain_check:
                # * There is a point in to_check.
                # and
                # * We are checking if it is a subdomain.

                # We return True, it is a subdomain.
                return True

            # We are not checking if it is a subdomain.

            if "." in to_check:
                # There is a point in to_check.

                # We check if it passes our subdomain regex.
                # * True: It's a valid domain.
                # * False: It's an invalid domain.
                return Regex(
                    to_check, regex_valid_subdomains, return_data=False
                ).match()

        except (ValueError, AttributeError):
            # In case of a value or attribute error we ignore them.
            pass

        # And we return False, the domain is not valid.
        return False


def is_subdomain(self, domain=None):
        """
        Check if the given subdomain is a subdomain.

        :param domain: The domain to validate.
        :type domain: str

        :return: The validity of the subdomain.
        :rtype: bool
        """

        if domain:
            # A domain is given.

            # We set the element to test as the parsed domain.
            to_test = domain
        elif self.element:
            # A domain is globally given.

            # We set the globally parsed domain.
            to_test = self.element
        else:
            # A domain is not given.

            # We set the element to test as the currently tested element.
            to_test = PyFunceble.INTERN["to_test"]

        # We return the status of the check.
        return self.is_domain_valid(to_test, subdomain_check=True)

def is_subdomain ( self , domain = None ) : if domain : to_test = domain elif self . element : to_test = self . element else : to_test = PyFunceble . INTERN [ "to_test" ] return self . is_domain_valid ( to_test , subdomain_check = True )

Check if the given subdomain is a subdomain .


def get(cls):
        """
        Execute the logic behind the Syntax handling.

        :return: The syntax status.
        :rtype: str
        """

        if PyFunceble.INTERN["to_test_type"] == "domain":
            # We are testing for domain or ip.

            if Check().is_domain_valid() or Check().is_ip_valid():
                # * The domain is valid.
                # or
                # * The IP is valid.

                # We handle and return the valid status.
                return SyntaxStatus(PyFunceble.STATUS["official"]["valid"]).handle()
        elif PyFunceble.INTERN["to_test_type"] == "url":
            # We are testing for URL.

            if Check().is_url_valid():
                # * The url is valid.

                # We handle and return the valid status.
                return SyntaxStatus(PyFunceble.STATUS["official"]["valid"]).handle()
        else:
            raise Exception("Unknow test type.")

        # We handle and return the invalid status.
        return SyntaxStatus(PyFunceble.STATUS["official"]["invalid"]).handle()

def
get
(
cls
)
:
if
PyFunceble
.
INTERN
[
"to_test_type"
]
==
"domain"
:
if
Check
(
)
.
is_domain_valid
(
)
or
Check
(
)
.
is_ip_valid
(
)
:
return
SyntaxStatus
(
PyFunceble
.
STATUS
[
"official"
]
[
"valid"
]
)
.
handle
(
)
elif
PyFunceble
.
INTERN
[
"to_test_type"
]
==
"url"
:
if
Check
(
)
.
is_url_valid
(
)
:
return
SyntaxStatus
(
PyFunceble
.
STATUS
[
"official"
]
[
"valid"
]
)
.
handle
(
)
else
:
raise
Exception
(
"Unknow test type."
)
return
SyntaxStatus
(
PyFunceble
.
STATUS
[
"official"
]
[
"invalid"
]
)
.
handle
(
)

Execute the logic behind the Syntax handling .