From ab4b918ba3a8b4f5ad5746fe77e564d3e1e38eee Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Mon, 13 Mar 2023 16:15:45 +0000 Subject: [PATCH] Improve detection of python projects (#134) --- .pre-commit-config.yaml | 5 +- samples/integration/podman.txt | 3 -- samples/integration/typeshed.txt | 3 -- src/mk/__main__.py | 9 +++- src/mk/tools/py_package.py | 93 ++++++++++++++++++++++---------- 5 files changed, 74 insertions(+), 39 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c5ab634..a63c8a6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -56,7 +56,7 @@ repos: types: [file, yaml] entry: yamllint --strict - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.0.1 + rev: v1.1.1 hooks: - id: mypy # mypy args needed in order to match mypy cli behavior @@ -64,6 +64,7 @@ repos: entry: mypy src/ pass_filenames: false additional_dependencies: + - build - diskcache - importlib-metadata - packaging @@ -71,7 +72,7 @@ repos: - subprocess-tee - typer>=0.7.0 - repo: https://github.com/pycqa/pylint - rev: v2.16.4 + rev: v2.17.0 hooks: - id: pylint additional_dependencies: diff --git a/samples/integration/podman.txt b/samples/integration/podman.txt index be52be9..cd51b41 100644 --- a/samples/integration/podman.txt +++ b/samples/integration/podman.txt @@ -1,18 +1,15 @@ binaries binaries2 -build clean clean-binaries docker docs help install -install2 lint local-cross package package-install podman-mac-helper test -uninstall up diff --git a/samples/integration/typeshed.txt b/samples/integration/typeshed.txt index 724ef71..b34d86d 100644 --- a/samples/integration/typeshed.txt +++ b/samples/integration/typeshed.txt @@ -1,7 +1,4 @@ -build create_baseline_stubs generate_proto_stubs -install lint -uninstall up diff --git a/src/mk/__main__.py b/src/mk/__main__.py index 5df82ec..deb61d3 100644 --- a/src/mk/__main__.py +++ b/src/mk/__main__.py @@ -3,6 +3,7 @@ import itertools import logging import os +import shlex from typing import Any, Dict, List import typer @@ -113,10 +114,14 @@ def cli() -> None: action.tool, action_name, ) - panel = "Detected commands" if action.tool else "" + # panel = "Detected commands" if action.tool else "" + panel = str(action.tool) or "foo" short_help = action.description or "" if action.tool: - short_help += f" [dim]({action.tool})[/dim]" + if action.args: + short_help += f" [dim]({shlex.join(action.args)})[/dim]" + else: + short_help += f" [dim]{action.tool}[/dim]" app.command( name=action_name, short_help=short_help, diff --git a/src/mk/tools/py_package.py b/src/mk/tools/py_package.py index 52c2611..93a1d5a 100644 --- a/src/mk/tools/py_package.py +++ b/src/mk/tools/py_package.py @@ -1,9 +1,10 @@ -import os +import logging import sys from pathlib import Path from typing import List, Optional from mk.exec import run_or_fail +from mk.loaders import load_toml from mk.tools import Action, Tool @@ -14,50 +15,84 @@ class PyPackageTool(Tool): def __init__(self) -> None: super().__init__(self) + self.pkg_name = "" def run(self, action: Optional[Action] = None) -> None: if not action: return - if action.name == "build": - cmd = [ - sys.executable, - "-m", - "build", - "--sdist", - "--wheel", - "--outdir", - "dist", - ] + if action.name in ["build", "install", "uninstall"]: + cmd = action.args run_or_fail(cmd, tee=True) run_or_fail(f"{sys.executable} -m twine check dist/*", tee=True) - elif action.name == "install": - cmd = [sys.executable, "-m", "pip", action.name, "-e", "."] - run_or_fail(cmd, tee=True) - elif action.name == "uninstall": - pkg_name = run_or_fail( - [sys.executable, "setup.py", "--name"], tee=False - ).stdout - cmd = [sys.executable, "-m", "pip", action.name, "-y", pkg_name] - run_or_fail(cmd, tee=True) + else: + raise NotImplementedError(f"Action {action.name} not implemented") def is_present(self, path: Path) -> bool: - for name in ("setup.cfg", "setup.py", "pyproject.toml"): - if os.path.isfile(name): + data = load_toml(path / "pyproject.toml") + self.pkg_name = ( + data.get("project", {}).get("name", "") + or data.get("tool", {}) + .get("flit", {}) + .get("metadata", {}) + .get("module", "") + or data.get("tool", {}).get("poetry", {}).get("name", "") + ) + if not self.pkg_name: + if (path / "setup.py").exists(): + self.pkg_name = run_or_fail( + [sys.executable, "setup.py", "--name"], tee=False + ).stdout.strip() + return True + if (path / "setup.cfg").exists(): return True - return False + return False + return True def actions(self) -> List[Action]: - actions = [] + actions: List[Action] = [] + if not self.is_present(Path(".")): + return actions - for name in ("install", "uninstall"): - description = f"Use pip to {name} current package" - actions.append(Action(name=name, tool=self, description=description)) actions.append( Action( - name="build", + name="install", tool=self, - description="Use python build to produce sdist and wheel, followed by twine check", + description="Use pip to install the current package for current user.", + args=["pip3", "install", "-e", "."], ) ) + if self.pkg_name: + actions.append( + Action( + name="uninstall", + tool=self, + description="Use pip to uninstall the current package.", + args=["pip3", "uninstall", self.pkg_name], + ) + ) + try: + # pylint: disable=import-outside-toplevel,unused-import + import build # noqa: F401 + + actions.append( + Action( + name="build", + tool=self, + description="Use python build to produce sdist and wheel, followed by twine check", + args=[ + "python3", + "-m", + "build", + "--sdist", + "--wheel", + "--outdir", + "dist", + ], + ) + ) + except ImportError: + logging.warning( + "Python 'build' package not found, unable to provide build action." + ) return actions