#     Copyright 2026, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file


"""Standard plug-in to handle pkg_resource special needs.

Nuitka can detect some things that "pkg_resources" may not even be able to during
runtime, but that is done by nodes and optimization. But there are other things,
that need special case, e.g. the registration of the loader class.
"""

import re

from nuitka.__past__ import PermissionError  # pylint: disable=redefined-builtin
from nuitka.plugins.PluginBase import NuitkaPluginBase
from nuitka.PythonVersions import python_version
from nuitka.utils.Utils import withNoDeprecationWarning, withNoWarning


class NuitkaPluginResources(NuitkaPluginBase):
    plugin_name = "pkg-resources"
    plugin_desc = "Required by 'pkg_resources' package."
    plugin_category = "package-support"

    def __init__(self):
        with withNoWarning():
            try:
                import pkg_resources
            except (ImportError, RuntimeError, PermissionError):
                self.pkg_resources = None
            else:
                self.pkg_resources = pkg_resources

        try:
            if python_version >= 0x380:
                from importlib import metadata
            else:
                import importlib_metadata as metadata
        except (ImportError, SyntaxError, RuntimeError):
            self.metadata = None
        else:
            self.metadata = metadata

    @staticmethod
    def isAlwaysEnabled():
        return True

    def _handleEasyInstallEntryScript(self, dist, group, name):
        module_name = None
        main_name = None

        # First try metadata, which is what the runner also does first.
        if self.metadata:
            dist = self.metadata.distribution(dist.partition("==")[0])

            for entry_point in dist.entry_points:
                if entry_point.group == group and entry_point.name == name:
                    try:
                        module_name = entry_point.module
                        main_name = entry_point.attr
                    except AttributeError:
                        match = entry_point.pattern.match(entry_point.value)

                        module_name = match.group("module")
                        main_name = match.group("attr")

                    break

        if module_name is None and self.pkg_resources:
            with withNoDeprecationWarning():
                entry_point = self.pkg_resources.get_entry_info(dist, group, name)

            module_name = entry_point.module_name
            main_name = entry_point.name

        if module_name is None:
            self.sysexit(
                "Error, failed to resolve easy install entry script, is the installation broken?"
            )

        return r"""
import sys, re
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
import %(module_name)s
sys.exit(%(module_name)s.%(main_name)s)
""" % {
            "module_name": module_name,
            "main_name": main_name,
        }

    def onModuleSourceCode(self, module_name, source_filename, source_code):
        if module_name == "__main__":
            match = re.search(
                "\n# EASY-INSTALL-ENTRY-SCRIPT: '(.*?)','(.*?)','(.*?)'", source_code
            )

            if match is not None:
                self.info(
                    "Detected easy install entry script, compile time detecting entry point."
                )

                return self._handleEasyInstallEntryScript(*match.groups())

        return source_code

    def createPostModuleLoadCode(self, module):
        """Create code to load after a module was successfully imported.

        For pkg_resources we need to register a provider.
        """
        if module.getFullName() == "pkg_resources":
            code = """\
from __future__ import absolute_import

import os
try:
    from pkg_resources import register_loader_type, EggProvider
except ImportError:
    pass
else:

    class NuitkaProvider(EggProvider):
        def _has(self, path):
            return os.path.exists(path)

        def _isdir(self, path):
            return os.path.isdir(path)

        def _listdir(self, path):
            return os.listdir(path)

        def get_resource_stream(self, manager, resource_name):
            return open(self._fn(self.module_path, resource_name), 'rb')

        def _get(self, path):
            with open(path, 'rb') as stream:
                return stream.read()

    assert __loader__.__class__.__name__ == "nuitka_module_loader", __loader__

    register_loader_type(__loader__.__class__, NuitkaProvider)
"""

            yield (
                code,
                """Registering Nuitka loader with "pkg_resources".""",
            )

    def decideCompilation(self, module_name):
        if module_name == "pkg_resources-postLoad":
            return "compiled"


#     Part of "Nuitka", an optimizing Python compiler that is compatible and
#     integrates with CPython, but also works on its own.
#
#     Licensed under the GNU Affero General Public License, Version 3 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#        https://www.gnu.org/licenses/agpl-3.0.txt
#
#     See also: "Nuitka Runtime Library Exception, Version 1.0" in file
#     "LICENSE-RUNTIME.txt" for additional permissions granted under Section 7.
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
