Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ dev = ["pytest>=6", "pytest-cov", "pytest-timeout"]
[project.scripts]
shtab = "shtab.main:main"

[project.entry-points."setuptools.finalize_distribution_options"]
generate = "shtab.setuptools:generate_completions"

[tool.flake8]
max_line_length = 99
extend_ignore = ["E261", "P101"]
Expand Down
80 changes: 80 additions & 0 deletions shtab/setuptools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""A entry point for setuptools."""
import io
import os
import sys
from contextlib import redirect_stdout, suppress
from importlib import import_module
from importlib.metadata import EntryPoint, EntryPoints
from typing import Callable

Check warning on line 8 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L2-L8

Added lines #L2 - L8 were not covered by tests

from setuptools import Distribution

Check warning on line 10 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L10

Added line #L10 was not covered by tests

try:
import tomllib as tomli
except ImportError:
import tomli

Check warning on line 15 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L12-L15

Added lines #L12 - L15 were not covered by tests


def get_stdout(function: Callable) -> str:

Check warning on line 18 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L18

Added line #L18 was not covered by tests
"""Get stdout."""
string = io.StringIO()
with redirect_stdout(string), suppress(SystemExit):
function()
string.seek(0)
content = string.read()
return content

Check warning on line 25 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L20-L25

Added lines #L20 - L25 were not covered by tests


def generate_completions(distribution: Distribution) -> None:

Check warning on line 28 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L28

Added line #L28 was not covered by tests
"""Generate completions."""
# Get entry_points from setup.py
entry_points = getattr(distribution, "entry_points")
if entry_points is None:
entry_points = EntryPoints()
entry_points = entry_points.select(group="console_scripts")

Check warning on line 34 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L31-L34

Added lines #L31 - L34 were not covered by tests
# Get entry_points from setup.cfg
if len(entry_points) == 0 and os.path.isfile("setup.cfg"):
import configparser

Check warning on line 37 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L36-L37

Added lines #L36 - L37 were not covered by tests

parser = configparser.ConfigParser()
parser.read(["setup.cfg"], encoding="utf-8")
_entry_points = parser.get("metadata", "entry_points", fallback=None)
if isinstance(_entry_points, dict):
console_scripts = _entry_points.get("console_scripts", [])
for console_script in console_scripts:
k, _, v = console_script.partition("=")
entry_points += [

Check warning on line 46 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L39-L46

Added lines #L39 - L46 were not covered by tests
EntryPoint(name=k.strip(), group="console_scripts", value=v.strip())]
# Get entry_points from pyproject.toml
if len(entry_points) == 0 and os.path.isfile("pyproject.toml"):
with open("pyproject.toml", "rb") as f:
pyproject = tomli.load(f)
scripts = pyproject.get("project", {}).get("scripts", {})
if isinstance(scripts, dict):
for k, v in scripts.items():
entry_points += [EntryPoint(name=k, group="scripts", value=v)]

Check warning on line 55 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L49-L55

Added lines #L49 - L55 were not covered by tests

cwd = os.getcwd()

Check warning on line 57 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L57

Added line #L57 was not covered by tests
# https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#flat-layout
sys.path.insert(0, cwd)

Check warning on line 59 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L59

Added line #L59 was not covered by tests
# https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#src-layout
sys.path.insert(0, os.path.join(cwd, "src"))
for entry_point in entry_points:

Check warning on line 62 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L61-L62

Added lines #L61 - L62 were not covered by tests
# entry_point.value can be 'module_path:function_name[extra1, extra2]'
path = entry_point.value.split("[")[0]
module_path, _, function_name = path.rpartition(":")
module = import_module(module_path)
function = vars(module).get(function_name)
prog = entry_point.name
shells = {"bash": prog, "zsh": "_" + prog, "tcsh": prog + ".csh"}
os.makedirs("sdist", exist_ok=True)
argv = sys.argv
for shell, filename in shells.items():
sys.argv = [prog, "--print-completion", shell]

Check warning on line 73 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L64-L73

Added lines #L64 - L73 were not covered by tests
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not all programs use this. It may be e.g.:

Suggested change
sys.argv = [prog, "--print-completion", shell]
sys.argv = [prog, "complete", shell]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, It's a fast dirty trial 😄 How can we get the command option about print completion or get the completion content directly from python?

# bash, zsh, tcsh only use `\n`
content = get_stdout(function).replace("\r\n", "\n") # type: ignore
if content == "":
continue
with open(os.path.join("sdist", filename), "w", newline="") as f:
f.write(content)
sys.argv = argv

Check warning on line 80 in shtab/setuptools.py

View check run for this annotation

Codecov / codecov/patch

shtab/setuptools.py#L75-L80

Added lines #L75 - L80 were not covered by tests