From bb44c0e9ed525edc1fc3eb411685ecb8283244cc Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 12 Jul 2024 11:41:31 -0400 Subject: [PATCH 01/15] generate recipe from project tree using pypa/build --- grayskull/base/factory.py | 2 + grayskull/config.py | 1 + grayskull/main.py | 15 ++++++- grayskull/strategy/py_build.py | 78 ++++++++++++++++++++++++++++++++++ grayskull/utils.py | 8 +++- pyproject.toml | 3 +- 6 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 grayskull/strategy/py_build.py diff --git a/grayskull/base/factory.py b/grayskull/base/factory.py index d78a5a630..56113418e 100644 --- a/grayskull/base/factory.py +++ b/grayskull/base/factory.py @@ -7,12 +7,14 @@ from souschef.recipe import Recipe from grayskull.strategy.cran import CranStrategy +from grayskull.strategy.py_build import PyBuild from grayskull.strategy.pypi import PypiStrategy class GrayskullFactory(ABC): REGISTERED_STRATEGY = { "pypi": PypiStrategy, + "pybuild": PyBuild, "cran": CranStrategy, } diff --git a/grayskull/config.py b/grayskull/config.py index 544095028..6956b4d9a 100644 --- a/grayskull/config.py +++ b/grayskull/config.py @@ -42,6 +42,7 @@ class Configuration: is_arch: bool = False repo_github: Optional[str] = None from_local_sdist: bool = False + from_tree: bool = False local_sdist: Optional[str] = None missing_deps: set = field(default_factory=set) extras_require_test: Optional[str] = None diff --git a/grayskull/main.py b/grayskull/main.py index 4c385e530..313791fa8 100644 --- a/grayskull/main.py +++ b/grayskull/main.py @@ -14,7 +14,12 @@ from grayskull.cli import CLIConfig from grayskull.cli.stdout import print_msg from grayskull.config import Configuration -from grayskull.utils import generate_recipe, origin_is_github, origin_is_local_sdist +from grayskull.utils import ( + generate_recipe, + origin_is_github, + origin_is_local_sdist, + origin_is_tree, +) init(autoreset=True) logging.basicConfig(format="%(levelname)s:%(message)s") @@ -283,10 +288,13 @@ def generate_recipes_from_list(list_pkgs, args): for pkg_name in list_pkgs: logging.debug(f"Starting grayskull for pkg: {pkg_name}") from_local_sdist = origin_is_local_sdist(pkg_name) + from_tree = origin_is_tree(pkg_name) if origin_is_github(pkg_name): pypi_label = "" elif from_local_sdist: pypi_label = " (local)" + elif from_tree: + pypi_label = " (tree)" else: pypi_label = " (pypi)" print_msg( @@ -304,6 +312,7 @@ def generate_recipes_from_list(list_pkgs, args): url_pypi_metadata=args.url_pypi_metadata, sections_populate=args.sections_populate, from_local_sdist=from_local_sdist, + from_tree=from_tree, extras_require_test=args.extras_require_test, github_release_tag=args.github_release_tag, extras_require_include=tuple(args.extras_require_include), @@ -333,7 +342,9 @@ def create_python_recipe(pkg_name, sections_populate=None, **kwargs): config = Configuration(name=pkg_name, **kwargs) return ( GrayskullFactory.create_recipe( - "pypi", config, sections_populate=sections_populate + "pybuild" if config.from_tree else "pypi", + config, + sections_populate=sections_populate, ), config, ) diff --git a/grayskull/strategy/py_build.py b/grayskull/strategy/py_build.py new file mode 100644 index 000000000..004e3d091 --- /dev/null +++ b/grayskull/strategy/py_build.py @@ -0,0 +1,78 @@ +""" +Use pypa/build to get project metadata from a checkout. Create a recipe suitable +for inlinining into the first-party project source tree. +""" + +import tempfile +from importlib.metadata import PathDistribution +from pathlib import Path + +from packaging.markers import Marker +from packaging.metadata import Metadata +from souschef.recipe import Recipe + +import build +from grayskull.config import Configuration +from grayskull.strategy.abstract_strategy import AbstractStrategy +import logging + +log = logging.getLogger(__name__) + + +class PyBuild(AbstractStrategy): + @staticmethod + def fetch_data(recipe: Recipe, config: Configuration, sections=None): + project = build.ProjectBuilder(config.name) + + with tempfile.TemporaryDirectory(prefix="grayskull") as output: + build_system_requires = project.build_system_requires + requires_for_build = project.get_requires_for_build("wheel") + # If those are already installed, we can get the extras requirements + # without invoking pip e.g. setuptools_scm[toml] + print("Requires for build:", build_system_requires, requires_for_build) + + recipe["requirements"]["host"] = sorted( + (*build_system_requires, *requires_for_build) + ) + + # build the project's metadata "dist-info" directory + metadata_path = Path(project.metadata_path(output_directory=output)) + + distribution = PathDistribution(metadata_path) + + # real distribution name not pathname + config.name = distribution.name # see also _normalized_name + + # grayskull thought the name was the path. correct that. + if recipe[0] == '#% set name = "." %}': # XXX fragile + # recipe[0] = x does not work + recipe._yaml._yaml_get_pre_comment()[ + 0 + ].value = f'#% set name = "{config.name}" %}}' + elif config.name not in recipe[0]: + log.warning("Package name not found in first line of recipe") + + config.version = distribution.version + requires_dist = distribution.requires # includes extras as markers e.g. ; extra == 'testing'. Evaluate using Marker(). + entry_points = ( + distribution.entry_points + ) # list(EntryPoint(name, value, group) + + # distribution.metadata.keys() for grayskull is + # Metadata-Version + # Name + # Version + # Summary + # Author-email + # License + # Project-URL + # Keywords + # Requires-Python + # Description-Content-Type + # License-File + # License-File + # Requires-Dist (many times) + # Provides-Extra (several times) + # Description or distribution.metadata.get_payload() + + # raise NotImplementedError() diff --git a/grayskull/utils.py b/grayskull/utils.py index 8bf70972c..8b9e75d7f 100644 --- a/grayskull/utils.py +++ b/grayskull/utils.py @@ -108,6 +108,12 @@ def origin_is_local_sdist(name: str) -> bool: ) +def origin_is_tree(name: str) -> bool: + """Return True if it is a directory""" + path = Path(name) + return path.is_dir() + + def sha256_checksum(filename, block_size=65536): sha256 = hashlib.sha256() with open(filename, "rb") as f: @@ -214,7 +220,7 @@ def generate_recipe( recipe_dir = Path(folder_path) / pkg_name logging.debug(f"Generating recipe on: {recipe_dir}") if not recipe_dir.is_dir(): - recipe_dir.mkdir() + recipe_dir.mkdir(parents=True) recipe_path = recipe_dir / "meta.yaml" recipe_folder = recipe_dir add_new_lines_after_section(recipe.yaml) diff --git a/pyproject.toml b/pyproject.toml index 85c87b4e0..a765c12f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ dynamic = ["version"] requires-python = ">=3.8" dependencies = [ "beautifulsoup4", + "build", # conda install python-build "colorama", "conda-souschef >=2.2.3", "packaging >=21.3", @@ -52,8 +53,6 @@ docs = [ [project.scripts] grayskull = "grayskull.main:main" -greyskull = "grayskull.main:main" -conda-grayskull = "grayskull.main:main" conda-greyskull = "grayskull.main:main" [project.urls] From 09485b344afcf27575bacd263d4ec6bb41934eff Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:53:09 +0000 Subject: [PATCH 02/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- grayskull/strategy/py_build.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/grayskull/strategy/py_build.py b/grayskull/strategy/py_build.py index 004e3d091..7fc6cbcae 100644 --- a/grayskull/strategy/py_build.py +++ b/grayskull/strategy/py_build.py @@ -3,18 +3,18 @@ for inlinining into the first-party project source tree. """ +import logging import tempfile from importlib.metadata import PathDistribution from pathlib import Path +import build from packaging.markers import Marker from packaging.metadata import Metadata from souschef.recipe import Recipe -import build from grayskull.config import Configuration from grayskull.strategy.abstract_strategy import AbstractStrategy -import logging log = logging.getLogger(__name__) @@ -53,7 +53,9 @@ def fetch_data(recipe: Recipe, config: Configuration, sections=None): log.warning("Package name not found in first line of recipe") config.version = distribution.version - requires_dist = distribution.requires # includes extras as markers e.g. ; extra == 'testing'. Evaluate using Marker(). + requires_dist = ( + distribution.requires + ) # includes extras as markers e.g. ; extra == 'testing'. Evaluate using Marker(). entry_points = ( distribution.entry_points ) # list(EntryPoint(name, value, group) From bbbf2590d3957d2e9f10eaffd12eed678312c54c Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 12 Jul 2024 13:32:27 -0400 Subject: [PATCH 03/15] approach grayskull (pypi)'s result --- grayskull/strategy/py_build.py | 100 +++++++++++++++++++++++++++------ pyproject.toml | 7 ++- 2 files changed, 87 insertions(+), 20 deletions(-) diff --git a/grayskull/strategy/py_build.py b/grayskull/strategy/py_build.py index 7fc6cbcae..a22dd922c 100644 --- a/grayskull/strategy/py_build.py +++ b/grayskull/strategy/py_build.py @@ -8,13 +8,15 @@ from importlib.metadata import PathDistribution from pathlib import Path -import build -from packaging.markers import Marker -from packaging.metadata import Metadata +from conda.exceptions import InvalidMatchSpec +from conda.models.match_spec import MatchSpec +from packaging.requirements import Requirement from souschef.recipe import Recipe +import build from grayskull.config import Configuration from grayskull.strategy.abstract_strategy import AbstractStrategy +from grayskull.strategy.pypi import compose_test_section log = logging.getLogger(__name__) @@ -24,6 +26,9 @@ class PyBuild(AbstractStrategy): def fetch_data(recipe: Recipe, config: Configuration, sections=None): project = build.ProjectBuilder(config.name) + recipe["source"]["path"] = "../" + # XXX relative to output. "git_url: ../" is also a good choice. + with tempfile.TemporaryDirectory(prefix="grayskull") as output: build_system_requires = project.build_system_requires requires_for_build = project.get_requires_for_build("wheel") @@ -31,10 +36,6 @@ def fetch_data(recipe: Recipe, config: Configuration, sections=None): # without invoking pip e.g. setuptools_scm[toml] print("Requires for build:", build_system_requires, requires_for_build) - recipe["requirements"]["host"] = sorted( - (*build_system_requires, *requires_for_build) - ) - # build the project's metadata "dist-info" directory metadata_path = Path(project.metadata_path(output_directory=output)) @@ -42,23 +43,67 @@ def fetch_data(recipe: Recipe, config: Configuration, sections=None): # real distribution name not pathname config.name = distribution.name # see also _normalized_name + config.version = distribution.version # grayskull thought the name was the path. correct that. if recipe[0] == '#% set name = "." %}': # XXX fragile # recipe[0] = x does not work - recipe._yaml._yaml_get_pre_comment()[ - 0 - ].value = f'#% set name = "{config.name}" %}}' - elif config.name not in recipe[0]: + recipe._yaml._yaml_get_pre_comment()[0].value = ( + f'#% set name = "{config.name}" %}}\n' + f'#% set version = "{config.version}" %}}\n' + ) + + recipe["package"]["version"] = "{{ version }}" + elif config.name not in str(recipe[0]): log.warning("Package name not found in first line of recipe") - config.version = distribution.version - requires_dist = ( - distribution.requires - ) # includes extras as markers e.g. ; extra == 'testing'. Evaluate using Marker(). - entry_points = ( - distribution.entry_points - ) # list(EntryPoint(name, value, group) + metadata = distribution.metadata + + requires_python = metadata["requires-python"] + if requires_python: + requires_python = f"python { requires_python }" + else: + requires_python = "python" + + recipe["requirements"]["host"] = [requires_python] + sorted( + (*build_system_requires, *requires_for_build) + ) + + requirements = [Requirement(r) for r in distribution.requires] + active_requirements = [ + str(r).rsplit(";", 1)[0] + for r in requirements + if not r.marker or r.marker.evaluate() + ] + # XXX to normalize space between name and version, MatchSpec(r).spec + normalized_requirements = [] + for requirement in active_requirements: + try: + normalized_requirements.append( + # MatchSpec uses a metaclass hiding its constructor from + # the type checker + MatchSpec(requirement).spec # type: ignore + ) + except InvalidMatchSpec: + log.warning("%s is not a valid MatchSpec", requirement) + normalized_requirements.append(requirement) + + # conda does support ~=3.0.0 "compatibility release" matches + recipe["requirements"]["run"] = [requires_python] + normalized_requirements + # includes extras as markers e.g. ; extra == 'testing'. Evaluate + # using Marker(). + + recipe["build"]["entry_points"] = [ + f"{ep.name} = {ep.value}" + for ep in distribution.entry_points + if ep.group == "console_scripts" + ] + + recipe["build"]["noarch"] = "python" + recipe["build"][ + "script" + ] = "{{ PYTHON }} -m pip install . -vv --no-deps --no-build-isolation" + # XXX also --no-index? # distribution.metadata.keys() for grayskull is # Metadata-Version @@ -77,4 +122,23 @@ def fetch_data(recipe: Recipe, config: Configuration, sections=None): # Provides-Extra (several times) # Description or distribution.metadata.get_payload() + about = { + "summary": metadata["summary"], + "license": metadata["license"], + # there are two license-file in grayskull e.g. + "license_file": metadata["license-file"], + } + recipe["about"] = about + + metadata_dict = dict( + metadata + ) # XXX not what compose_test_section expects at all + metadata_dict["name"] = config.name + metadata_dict["entry_points"] = [ + f"{ep.name} = {ep.value}" + for ep in distribution.entry_points + if ep.group == "console_scripts" + ] + recipe["test"] = compose_test_section(metadata_dict, []) + # raise NotImplementedError() diff --git a/pyproject.toml b/pyproject.toml index a765c12f9..265cbcd27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,9 +51,12 @@ docs = [ "mdit-py-plugins>=0.3.0", ] +# both spellings of gray/grey [project.scripts] -grayskull = "grayskull.main:main" -conda-greyskull = "grayskull.main:main" +grayskull = "grayskull.__main__:main" +greyskull = "grayskull.__main__:main" +conda-grayskull = "grayskull.__main__:main" +conda-greyskull = "grayskull.__main__:main" [project.urls] Source = "https://github.com/conda/grayskull" From c712fe9b13f50f00b8237b4f87cae0e3d82c78c9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 17:32:41 +0000 Subject: [PATCH 04/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- grayskull/strategy/py_build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grayskull/strategy/py_build.py b/grayskull/strategy/py_build.py index a22dd922c..cc5370dfd 100644 --- a/grayskull/strategy/py_build.py +++ b/grayskull/strategy/py_build.py @@ -8,12 +8,12 @@ from importlib.metadata import PathDistribution from pathlib import Path +import build from conda.exceptions import InvalidMatchSpec from conda.models.match_spec import MatchSpec from packaging.requirements import Requirement from souschef.recipe import Recipe -import build from grayskull.config import Configuration from grayskull.strategy.abstract_strategy import AbstractStrategy from grayskull.strategy.pypi import compose_test_section From e967d1cef5aa3d734f6eca714929a0e5b53482bd Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 12 Jul 2024 13:43:09 -0400 Subject: [PATCH 05/15] allow missing conda dependency --- grayskull/base/factory.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/grayskull/base/factory.py b/grayskull/base/factory.py index 56113418e..8012f995f 100644 --- a/grayskull/base/factory.py +++ b/grayskull/base/factory.py @@ -7,17 +7,22 @@ from souschef.recipe import Recipe from grayskull.strategy.cran import CranStrategy -from grayskull.strategy.py_build import PyBuild +try: + from grayskull.strategy.py_build import PyBuild +except ImportError: # requires conda + PyBuild = None from grayskull.strategy.pypi import PypiStrategy class GrayskullFactory(ABC): REGISTERED_STRATEGY = { "pypi": PypiStrategy, - "pybuild": PyBuild, "cran": CranStrategy, } + if PyBuild: + REGISTERED_STRATEGY["pybuild"] = PyBuild + @staticmethod def create_recipe(repo_type: str, config, pkg_name=None, sections_populate=None): if repo_type.lower() not in GrayskullFactory.REGISTERED_STRATEGY: From e262ec9f0f1a60e24b5c15d5a18afa5b4909554d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 17:43:34 +0000 Subject: [PATCH 06/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- grayskull/base/factory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grayskull/base/factory.py b/grayskull/base/factory.py index 8012f995f..4b25e23d3 100644 --- a/grayskull/base/factory.py +++ b/grayskull/base/factory.py @@ -7,9 +7,10 @@ from souschef.recipe import Recipe from grayskull.strategy.cran import CranStrategy + try: from grayskull.strategy.py_build import PyBuild -except ImportError: # requires conda +except ImportError: # requires conda PyBuild = None from grayskull.strategy.pypi import PypiStrategy From ad98bb692936498adc189b121d86ad93525bb2ed Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 12 Jul 2024 13:57:05 -0400 Subject: [PATCH 07/15] fallback if requires is None --- grayskull/strategy/py_build.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grayskull/strategy/py_build.py b/grayskull/strategy/py_build.py index cc5370dfd..7070300c9 100644 --- a/grayskull/strategy/py_build.py +++ b/grayskull/strategy/py_build.py @@ -69,7 +69,7 @@ def fetch_data(recipe: Recipe, config: Configuration, sections=None): (*build_system_requires, *requires_for_build) ) - requirements = [Requirement(r) for r in distribution.requires] + requirements = [Requirement(r) for r in distribution.requires or []] active_requirements = [ str(r).rsplit(";", 1)[0] for r in requirements @@ -100,9 +100,9 @@ def fetch_data(recipe: Recipe, config: Configuration, sections=None): ] recipe["build"]["noarch"] = "python" - recipe["build"][ - "script" - ] = "{{ PYTHON }} -m pip install . -vv --no-deps --no-build-isolation" + recipe["build"]["script"] = ( + "{{ PYTHON }} -m pip install . -vv --no-deps --no-build-isolation" + ) # XXX also --no-index? # distribution.metadata.keys() for grayskull is From 8ef8e256d77d34e72ad8ee75662560622ddd2c73 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 17:57:18 +0000 Subject: [PATCH 08/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- grayskull/strategy/py_build.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grayskull/strategy/py_build.py b/grayskull/strategy/py_build.py index 7070300c9..a80024c0f 100644 --- a/grayskull/strategy/py_build.py +++ b/grayskull/strategy/py_build.py @@ -100,9 +100,9 @@ def fetch_data(recipe: Recipe, config: Configuration, sections=None): ] recipe["build"]["noarch"] = "python" - recipe["build"]["script"] = ( - "{{ PYTHON }} -m pip install . -vv --no-deps --no-build-isolation" - ) + recipe["build"][ + "script" + ] = "{{ PYTHON }} -m pip install . -vv --no-deps --no-build-isolation" # XXX also --no-index? # distribution.metadata.keys() for grayskull is From 5f3973247db8a1041f3a8e7110a75b76ad810603 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 12 Jul 2024 17:02:41 -0400 Subject: [PATCH 09/15] expect additional argument in spy --- tests/cli/test_cli_cmds.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/cli/test_cli_cmds.py b/tests/cli/test_cli_cmds.py index 009c64af0..48d0b367a 100644 --- a/tests/cli/test_cli_cmds.py +++ b/tests/cli/test_cli_cmds.py @@ -79,6 +79,7 @@ def test_change_pypi_url(mocker): url_pypi_metadata="http://url_pypi.com/abc", sections_populate=None, from_local_sdist=False, + from_tree=False, extras_require_test=None, github_release_tag=None, extras_require_include=tuple(), From ec43149b5870436055dae28f58d54e6229cdffe7 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 12 Jul 2024 17:24:59 -0400 Subject: [PATCH 10/15] find extras example --- grayskull/strategy/py_build.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/grayskull/strategy/py_build.py b/grayskull/strategy/py_build.py index a80024c0f..a3f67f251 100644 --- a/grayskull/strategy/py_build.py +++ b/grayskull/strategy/py_build.py @@ -8,12 +8,12 @@ from importlib.metadata import PathDistribution from pathlib import Path -import build from conda.exceptions import InvalidMatchSpec from conda.models.match_spec import MatchSpec from packaging.requirements import Requirement from souschef.recipe import Recipe +import build from grayskull.config import Configuration from grayskull.strategy.abstract_strategy import AbstractStrategy from grayskull.strategy.pypi import compose_test_section @@ -36,6 +36,18 @@ def fetch_data(recipe: Recipe, config: Configuration, sections=None): # without invoking pip e.g. setuptools_scm[toml] print("Requires for build:", build_system_requires, requires_for_build) + # Example of finding extra dependencies for a distribution 'scm' (is + # there a dict API?) Subtract "has non-extra marker" dependencies from + # this set. + # + # for e in scm.metadata.get_all('provides-extra'): + # print (e, [x for x in r if x.marker and x.marker.evaluate({'extra':e})]) + # + # docs [, , , , , ] + # rich [] + # test [, , , ] + # toml [] + # build the project's metadata "dist-info" directory metadata_path = Path(project.metadata_path(output_directory=output)) From 2734288147b0343432cffbc352949e56d8c28675 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 21:26:31 +0000 Subject: [PATCH 11/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- grayskull/strategy/py_build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grayskull/strategy/py_build.py b/grayskull/strategy/py_build.py index a3f67f251..1f0d94f2b 100644 --- a/grayskull/strategy/py_build.py +++ b/grayskull/strategy/py_build.py @@ -8,12 +8,12 @@ from importlib.metadata import PathDistribution from pathlib import Path +import build from conda.exceptions import InvalidMatchSpec from conda.models.match_spec import MatchSpec from packaging.requirements import Requirement from souschef.recipe import Recipe -import build from grayskull.config import Configuration from grayskull.strategy.abstract_strategy import AbstractStrategy from grayskull.strategy.pypi import compose_test_section From 9ba91d8f0b0f7a7222f1871a2e497ccb5b7206ca Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 12 Jul 2024 17:55:25 -0400 Subject: [PATCH 12/15] add simpler license matcher --- grayskull/license/discovery.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/grayskull/license/discovery.py b/grayskull/license/discovery.py index 3bac764e5..311100dae 100644 --- a/grayskull/license/discovery.py +++ b/grayskull/license/discovery.py @@ -62,6 +62,18 @@ def get_all_licenses_from_spdx() -> List: ] +def _match_scrambled_exact(candidate, licenses) -> str | None: + """ + Return license with rearranged word order only. + + Fancy scorer confuses BSD-3-Clause with DEC-3-Clause. + """ + bag = set(re.findall(r"\w+", candidate.lower())) + for license in licenses: + if bag == set(re.findall(r"\w+", license.lower())): + return license + + def match_license(name: str) -> dict: """Match if the given license name matches any license present on spdx.org @@ -75,11 +87,16 @@ def match_license(name: str) -> dict: name = re.sub(r"\s+license\s*", "", name.strip(), flags=re.IGNORECASE) name = name.strip() - best_matches = process.extract( - name, _get_all_license_choice(all_licenses), scorer=partial_ratio - ) - best_matches = process.extract(name, [lc for lc, *_ in best_matches]) - spdx_license = best_matches[0] + exact_match = _match_scrambled_exact(name, _get_all_license_choice(all_licenses)) + if exact_match: + best_matches = [(exact_match, 100, 0)] + spdx_license = best_matches[0] + else: + best_matches = process.extract( + name, _get_all_license_choice(all_licenses), scorer=partial_ratio + ) + best_matches = process.extract(name, [lc for lc, *_ in best_matches]) + spdx_license = best_matches[0] if spdx_license[1] < 100: # Prefer "-or-later" licenses over the "-only" From c6d2934d4b35274c7e8f8477d421d6c836f05a8a Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 12 Jul 2024 18:02:28 -0400 Subject: [PATCH 13/15] remove newer typing --- grayskull/license/discovery.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grayskull/license/discovery.py b/grayskull/license/discovery.py index 311100dae..6a3c0d462 100644 --- a/grayskull/license/discovery.py +++ b/grayskull/license/discovery.py @@ -62,7 +62,7 @@ def get_all_licenses_from_spdx() -> List: ] -def _match_scrambled_exact(candidate, licenses) -> str | None: +def _match_scrambled_exact(candidate, licenses): """ Return license with rearranged word order only. @@ -72,6 +72,7 @@ def _match_scrambled_exact(candidate, licenses) -> str | None: for license in licenses: if bag == set(re.findall(r"\w+", license.lower())): return license + return "" def match_license(name: str) -> dict: From a6ded19924afbac209a6abe6b03ff3ce09a849e3 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 12 Jul 2024 18:07:27 -0400 Subject: [PATCH 14/15] mention pkg_resources API --- grayskull/strategy/py_build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grayskull/strategy/py_build.py b/grayskull/strategy/py_build.py index 1f0d94f2b..c607babf9 100644 --- a/grayskull/strategy/py_build.py +++ b/grayskull/strategy/py_build.py @@ -40,6 +40,8 @@ def fetch_data(recipe: Recipe, config: Configuration, sections=None): # there a dict API?) Subtract "has non-extra marker" dependencies from # this set. # + # Easier API is deprecated pkg_resources.Distribution().requires(extras=()) + # # for e in scm.metadata.get_all('provides-extra'): # print (e, [x for x in r if x.marker and x.marker.evaluate({'extra':e})]) # From 523f893213f34b04557e7b339595099dc4b2251a Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Sat, 13 Jul 2024 19:34:58 -0400 Subject: [PATCH 15/15] pyproject --- pyproject.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 265cbcd27..55b1c2fce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,10 +53,10 @@ docs = [ # both spellings of gray/grey [project.scripts] -grayskull = "grayskull.__main__:main" -greyskull = "grayskull.__main__:main" -conda-grayskull = "grayskull.__main__:main" -conda-greyskull = "grayskull.__main__:main" +grayskull = "grayskull.main:main" +greyskull = "grayskull.main:main" +conda-grayskull = "grayskull.main:main" +conda-greyskull = "grayskull.main:main" [project.urls] Source = "https://github.com/conda/grayskull"