Repository: conda-incubator/grayskull (python)

Issue:
[BUG] Grayskull detects debian/copyright as a license file
**Describe the bug**
`grayskull` 1.8.3 detects `debian/copyright` in a tarball as a 'license' and includes that in the conda recipe. I think it shouldn't.

**To Reproduce**
Steps to reproduce the behavior:
```
mamba install grayskull=1.8.3
grayskull pypi gwosc==0.6.1
```

**Expected behavior**
The `debian/copyright` should not be discovered, and the only license file(s) should be the one(s) declared in the `License-File` metadata entry.

**Outputs**
```console
$ grayskull pypi gwosc==0.6.1
#### Initializing recipe for gwosc==0.6.1 (pypi) ####

Recovering metadata from pypi...
Starting the download of the sdist package gwosc
gwosc   0% ETA:  --:--:--   0.0 s/B|                                          |
gwosc 100% Time:  0:00:00   2.4 MiB/s|########################################|
Recovering information from setup.py
Executing injected distutils...
/home/duncan/opt/mambaforge/envs/grayskull/lib/python3.10/site-packages/setuptools/installer.py:27: SetuptoolsDeprecationWarning: setuptools.installer is deprecated. Requirements should be satisfied by a PEP 517 installer.
  warnings.warn(
Recovering metadata from setup.cfg
Checking >> --   0% |                                  |[Elapsed Time: 0:00:00]
Checking >> requests  50% |##############              |[Elapsed Time: 0:00:00]
Checking >> requests 100% |############################|[Elapsed Time: 0:00:00]
Recovering license info from spdx.org ...
INFO:Best match for license MIT was ('MIT', 100.0, 445).
Best matches: [('MIT', 100.0, 445), ('MIT-advertising', 90.0, 30), ('MIT-CMU', 90.0, 217), ('MIT-feh', 90.0, 405), ('No Limit Public License', 90.0, 416)]
Matching license file with database from Grayskull...
Match percentage of the license is 97%. Low match percentage could mean that the license was modified.Matching license file with database from Grayskull...
Match percentage of the license is 64%. Low match percentage could mean that the license was modified.Matching license file with database from Grayskull...
Match percentage of the license is 98%. Low match percentage could mean that the license was modified.Matching license file with database from Grayskull...
Match percentage of the license is 89%. Low match percentage could mean that the license was modified.License type: MIT
License file: ['LICENSE']
License type: Apache-2.0
License file: ['LICENSE', '.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/LICENSE']
License type: MIT
License file: ['LICENSE', '.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/LICENSE', '.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/LICENSE']
License type: MIT
License file: ['LICENSE', '.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/LICENSE', '.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/LICENSE', 'debian/copyright']
Multiple licenses detected! Please, be sure to check them.
Build requirements:
  <none>
Host requirements:
  - python >=3.5
  - setuptools-scm
  - setuptools >=38.2.5
  - wheel
  - pip
Run requirements:
  - python >=3.5
  - requests >=1.0.0

RED: Missing packages
GREEN: Packages available on conda-forge
Using default recipe maintainer: AddYourGitHubIdHere

Maintainers:
   - AddYourGitHubIdHere

#### Recipe generated on /home/duncan/tmp for gwosc==0.6.1 ####
```

**Environment:**
 - conda list
   ```console
   # packages in environment at /home/duncan/opt/mambaforge/envs/grayskull:
   #
   # Name                    Version                   Build  Channel
   _libgcc_mutex             0.1                 conda_forge    conda-forge
   _openmp_mutex             4.5                       2_gnu    conda-forge
   alabaster                 0.7.12                     py_0    conda-forge
   babel                     2.10.3             pyhd8ed1ab_0    conda-forge
   brotlipy                  0.7.0           py310h5764c6d_1004    conda-forge
   bzip2                     1.0.8                h7f98852_4    conda-forge
   c-ares                    1.18.1               h7f98852_0    conda-forge
   ca -certificates           2022.9.24            ha878542_0    conda-forge
   certifi                   2022.9.24          pyhd8ed1ab_0    conda-forge
   cffi                      1.15.1          py310h255011f_0    conda-forge
   charset-normalizer        2.1.1              pyhd8ed1ab_0    conda-forge
   colorama                  0.4.5              pyhd8ed1ab_0    conda-forge
   conda-souschef            2.2.3              pyhd8ed1ab_0    conda-forge
   cryptography              38.0.1          py310h597c629_0    conda-forge
   curl                      7.85.0               h7bff187_0    conda-forge
   docutils                  0.19            py310hff52083_0    conda-forge
   expat                     2.4.9                h27087fc_0    conda-forge
   gettext                   0.19.8.1          h27087fc_1009    conda-forge
   git                       2.38.0          pl5321h5fbbf19_0    conda-forge
   grayskull                 1.8.3              pyhd8ed1ab_0    conda-forge
   idna                      3.4                pyhd8ed1ab_0    conda-forge
   imagesize                 1.4.1              pyhd8ed1ab_0    conda-forge
   importlib-metadata        4.11.4          py310hff52083_0    conda-forge
   jarowinkler               1.2.3           py310hd8f1fbe_0    conda-forge
   jin ja2                    3.1.2              pyhd8ed1ab_1    conda-forge
   keyutils                  1.6.1                h166bdaf_0    conda-forge
   krb5                      1.19.3               h3790be6_0    conda-forge
   ld_impl_linux-64          2.36.1               hea4e1c9_2    conda-forge
   libblas                   3.9.0           16_linux64_openblas    conda-forge
   libcblas                  3.9.0           16_linux64_openblas    conda-forge
   libcurl                   7.85.0               h7bff187_0    conda-forge
   libedit                   3.1.20191231         he28a2e2_2    conda-forge
   libev                     4.33                 h516909a_1    conda-forge
   libffi                    3.4.2                h7f98852_5    conda-forge
   libgcc-ng                 12.1.0              h8d9b700_16    conda-forge
   libgfortran-ng            12.1.0              h69a702a_16    conda-forge
   libgfortran5              12.1.0              hdcd56e2_16    conda-forge
   libgomp                   12.1.0              h8d9b700_16    conda-forge
   libiconv                  1.17                 h166bdaf_0    conda-forge
   liblapack                 3.9.0           16_linux64_openblas    conda-forge
   libnghttp2                1.47.0               hdcd2b5c_1    conda-forge
   libnsl                    2.0.0                h7f98852_0    conda-forge
   libopenblas               0.3.21          pthreads_h78a6416_3    conda-forge
   libsqlite                 3.39.4               h753d276_0    conda-forge
   libssh2                   1.10.0               haa6b8db_3    conda-forge
   libstdcxx-ng              12.1.0              ha89aaad_16    conda-forge
   libuuid                   2.32.1            h7f98852_1000    conda-forge
   libzlib                   1.2.12               h166bdaf_4    conda-forge
   markupsafe                2.1.1           py310h5764c6d_1    conda-forge
   ncurses                   6.3                  h27087fc_1    conda-forge
   numpy                     1.23.3          py310h53a5b5f_0    conda-forge
   openssl                   1.1.1q               h166bdaf_0    conda-forge
   packaging                 21.3               pyhd8ed1ab_0    conda-forge
   pcre2                     10.37                hc3806b6_1    conda-forge
   perl                      5.32.1          2_h7f98852_perl5    conda-forge
   pip                       22.2.2             pyhd8ed1ab_0    conda-forge
   pkginfo                   1.8.3              pyhd8ed1ab_0    conda-forge
   progressbar2              4.0.0              pyhd8ed1ab_0    conda-forge
   pycparser                 2.21               pyhd8ed1ab_0    conda-forge
   pygments                  2.13.0             pyhd8ed1ab_0    conda-forge
   pyopenssl                 22.1.0             pyhd8ed1ab_0    conda-forge
   pyparsing                 3.0.9              pyhd8ed1ab_0    conda-forge
   pysocks                   1.7.1              pyha2e5f31_6    conda-forge
   python                    3.10.6          h582c2e5_0_cpython    conda-forge
   python-utils              3.3.3              pyhd8ed1ab_0    conda-forge
   python_abi                3.10                    2_cp310    conda-forge
   pytz                      2022.4             pyhd8ed1ab_0    conda-forge
   rapidfuzz                 2.11.1          py310hd8f1fbe_0    conda-forge
   readline                  8.1.2                h0f457ee_0    conda-forge
   requests                  2.28.1             pyhd8ed1ab_1    conda-forge
   ruamel.yaml               0.17.21         py310h5764c6d_1    conda-forge
   ruamel.yaml.clib          0.2.6           py310h5764c6d_1    conda-forge
   ruamel.yaml.jinja2        0.2.4                      py_1    conda-forge
   setuptools                65.4.1             pyhd8ed1ab_0    conda-forge
   snowballstemmer           2.2.0              pyhd8ed1ab_0    conda-forge
   sphinx                    5.2.3              pyhd8ed1ab_0    conda-forge
   sphinxcontrib-applehelp   1.0.2                      py_0    conda-forge
   sphinxcontrib-devhelp     1.0.2                      py_0    conda-forge
   sphinxcontrib-htmlhelp    2.0.0              pyhd8ed1ab_0    conda-forge
   sphinxcontrib-jsmath      1.0.1                      py_0    conda-forge
   sphinxcontrib-qthelp      1.0.3                      py_0    conda-forge
   sphinxcontrib-serializinghtml 1.1.5              pyhd8ed1ab_2    conda-forge
   stdlib-list               0.7.0                      py_2    conda-forge
   tk                        8.6.12               h27826a3_0    conda-forge
   tomli                     2.0.1              pyhd8ed1ab_0    conda-forge
   tomli-w                   1.0.0              pyhd8ed1ab_0    conda-forge
   tzdata                    2022d                h191b570_0    conda-forge
   urllib3                   1.26.11            pyhd8ed1ab_0    conda-forge
   wheel                     0.37.1             pyhd8ed1ab_0    conda-forge
   xz                        5.2.6                h166bdaf_0    conda-forge
   zipp                      3.9.0              pyhd8ed1ab_0    conda-forge
   ```
 - pip list
 - grayskull --version
   1.8.3

**Additional context**
Add any other context about the problem here.


Pull request:
Add option to exclude folder from search of licence
closes #401 

Patch:
diff --git a/grayskull/__main__.py b/grayskull/__main__.py
index cb2b416..908acf7 100644
--- a/grayskull/__main__.py
+++ b/grayskull/__main__.py
@@ -168,6 +168,13 @@ def main(args=None):
         help="Create a separate conda package for each extra requirements."
         " Ignored when no extra requirements are selected.",
     )
+    pypi_cmds.add_argument(
+        "--licence-exclude-folders",
+        default=tuple(),
+        nargs="*",
+        dest="licence_exclude_folders",
+        help="Exclude folders when searching for licence.",
+    )
 
     args = parser.parse_args(args)
 
@@ -225,6 +232,7 @@ def generate_recipes_from_list(list_pkgs, args):
                 extras_require_exclude=tuple(args.extras_require_exclude),
                 extras_require_all=args.extras_require_all,
                 extras_require_split=args.extras_require_split,
+                licence_exclude_folders=args.licence_exclude_folders,
             )
         except requests.exceptions.HTTPError as err:
             print_msg(f"{Fore.RED}Package seems to be missing.\nException: {err}\n\n")
diff --git a/grayskull/config.py b/grayskull/config.py
index 1d80625..5ed4c46 100644
--- a/grayskull/config.py
+++ b/grayskull/config.py
@@ -47,6 +47,7 @@ class Configuration:
     extras_require_exclude: Tuple[str] = tuple()
     extras_require_all: bool = False
     extras_require_split: bool = False
+    licence_exclude_folders: Tuple[str] = tuple()
 
     def get_oldest_py3_version(self, list_py_ver: List[PyVer]) -> PyVer:
         list_py_ver = sorted(list_py_ver)
diff --git a/grayskull/license/discovery.py b/grayskull/license/discovery.py
index 0aa1525..ae95d00 100644
--- a/grayskull/license/discovery.py
+++ b/grayskull/license/discovery.py
@@ -9,7 +9,7 @@ from operator import itemgetter
 from pathlib import Path
 from subprocess import check_output
 from tempfile import mkdtemp
-from typing import List, Optional, Union
+from typing import List, Optional, Tuple, Union
 
 import requests
 from colorama import Fore
@@ -183,6 +183,7 @@ def search_license_file(
     git_url: Optional[str] = None,
     version: Optional[str] = None,
     license_name_metadata: Optional[str] = None,
+    folders_exclude_search: Tuple[str] = tuple(),
 ) -> List[ShortLicense]:
     """Search for the license file. First it will try to find it in the given
     folder, after that it will search on the github api and for the last it will
@@ -197,7 +198,9 @@ def search_license_file(
     if license_name_metadata:
         license_name_metadata = get_short_license_id(license_name_metadata)
 
-    all_license_sdist = search_license_folder(folder_path, license_name_metadata)
+    all_license_sdist = search_license_folder(
+        folder_path, license_name_metadata, folders_exclude_search
+    )
     for license_sdist in all_license_sdist:
         license_sdist.is_packaged = True
         license_sdist.path = os.path.relpath(license_sdist.path, folder_path)
@@ -216,7 +219,9 @@ def search_license_file(
     if github_license:
         return [github_license]
 
-    repo_license = search_license_repo(git_url, version, license_name_metadata)
+    repo_license = search_license_repo(
+        git_url, version, license_name_metadata, folders_exclude_search
+    )
     if repo_license:
         return repo_license
     return [ShortLicense(license_name_metadata, None, False)]
@@ -269,7 +274,9 @@ def _get_api_github_url(github_url: str, version: Optional[str] = None) -> str:
 
 
 def search_license_folder(
-    path: Union[str, Path], default: Optional[str] = None
+    path: Union[str, Path],
+    default: Optional[str] = None,
+    folders_exclude_search: Tuple[str] = tuple(),
 ) -> List[ShortLicense]:
     """Search for the license in the given folder
 
@@ -277,6 +284,9 @@ def search_license_folder(
     :param default: Default value for the license type
     :return: License information
     """
+    folders_exclude_search = set(
+        list(folders_exclude_search) + ["doc", "theme", "themes", "docs"]
+    )
     re_search = re.compile(
         r"(\bcopyright\b|\bnotice\b|\blicense[s]*\b|\bcopying\b|\bcopyleft\b)",
         re.IGNORECASE,
@@ -286,8 +296,7 @@ def search_license_folder(
         dirnames[:] = [
             folder
             for folder in dirnames
-            if folder not in ("doc", "theme", "themes", "docs")
-            and not folder.startswith(".")
+            if folder not in folders_exclude_search and not folder.startswith(".")
         ]
         for one_file in filenames:
             if re_search.match(one_file):
@@ -299,13 +308,17 @@ def search_license_folder(
 
 
 def search_license_repo(
-    git_url: str, version: Optional[str], default: Optional[str] = None
+    git_url: str,
+    version: Optional[str],
+    default: Optional[str] = None,
+    folders_exclude_search: Tuple[str] = tuple(),
 ) -> Optional[List[ShortLicense]]:
     """Search for the license file in the given github repository
 
     :param git_url: GitHub URL
     :param version: Package version
     :param default: Default value for the license type
+    :param folders_exclude_search: Folders names to be excluded from search for licences
     :return: License information
     """
     git_url = re.sub(r"/$", ".git", git_url)
@@ -320,9 +333,13 @@ def search_license_repo(
             f" url: {git_url}, version: {version}. Exception: {err}"
         )
         if not version.startswith("v"):
-            return search_license_repo(git_url, f"v{version}", default)
+            return search_license_repo(
+                git_url, f"v{version}", default, folders_exclude_search
+            )
         return None
-    return search_license_folder(str(tmp_dir), default)
+    return search_license_folder(
+        str(tmp_dir), default, folders_exclude_search=folders_exclude_search
+    )
 
 
 def _get_git_cmd(git_url: str, version: str, dest) -> List[str]:


Test patch:
diff --git a/tests/cli/test_cli_cmds.py b/tests/cli/test_cli_cmds.py
index 08fa7b2..54d478f 100644
--- a/tests/cli/test_cli_cmds.py
+++ b/tests/cli/test_cli_cmds.py
@@ -85,6 +85,7 @@ def test_change_pypi_url(mocker):
         extras_require_exclude=tuple(),
         extras_require_all=False,
         extras_require_split=False,
+        licence_exclude_folders=tuple(),
     )
 
 
diff --git a/tests/license/test_discovery.py b/tests/license/test_discovery.py
index f8d9df0..861badf 100644
--- a/tests/license/test_discovery.py
+++ b/tests/license/test_discovery.py
@@ -139,6 +139,24 @@ def test_search_license_folder_hidden_folder(tmp_path, license_pytest_5_3_1):
     assert all_licenses[0].path == str(license_path)
 
 
+def test_search_licence_exclude_folders(tmp_path, license_pytest_5_3_1):
+    folder = tmp_path / "location-exclude-folder"
+    folder.mkdir()
+    folder_exclude = folder / "folder_exclude"
+    folder_exclude.mkdir()
+    (folder_exclude / "LICENSE").write_text("LICENCE TO EXCLUDE")
+    folder_search = folder / "folder_search"
+    folder_search.mkdir()
+    (folder_search / "LICENSE").write_text("LICENCE TO BE FOUND")
+    all_licences = search_license_folder(str(folder))
+    assert len(all_licences) == 2
+    all_licences = search_license_folder(
+        str(folder), folders_exclude_search=("folder_exclude",)
+    )
+    assert len(all_licences) == 1
+    assert all_licences[0].path == str(folder_search / "LICENSE")
+
+
 @pytest.mark.xfail(
     reason="This test may fail because github has limitation regarding the"
     " number of requisitions we can do to their api."