Skip to content

Commit

Permalink
exec: support kebab-case executables
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Dec 15, 2021
1 parent 295bc84 commit 458544f
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 9 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased
### Added
- Add support for `kebab-case` executable names. [#205](https://github.com/PyO3/setuptools-rust/pull/205)

## 1.1.2 (2021-12-05)
### Changed
- Removed dependency on `tomli` to simplify installation. [#200](https://github.com/PyO3/setuptools-rust/pull/200)
Expand Down
2 changes: 1 addition & 1 deletion examples/hello-world/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
version="1.0",
rust_extensions=[
RustExtension(
{"hello-world": "hello_world.hello_world"},
{"hello-world": "hello_world.hello-world"},
binding=Binding.Exec,
script=True,
)
Expand Down
40 changes: 32 additions & 8 deletions setuptools_rust/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ class RustExtension:
packages, i.e *not* a filename or pathname. It is possible to
specify multiple binaries, if extension uses ``Binding.Exec``
binding mode. In that case first argument has to be dictionary.
Keys of the dictionary corresponds to compiled rust binaries and
values are full name of the executable inside python package.
Keys of the dictionary correspond to the rust binary names and
values are the full dotted name to place the executable inside
the python package. To install executables with kebab-case names,
the final part of the dotted name can be in kebab-case. For
example, `hello_world.hello-world` will install an executable
named `hello-world`.
path: Path to the ``Cargo.toml`` manifest file.
args: A list of extra argumenents to be passed to Cargo. For example,
args: A list of extra arguments to be passed to Cargo. For example,
``args=["--no-default-features"]`` will disable the default
features listed in ``Cargo.toml``.
features: A list of Cargo features to also build.
Expand Down Expand Up @@ -169,19 +173,20 @@ def get_rust_version(self) -> Optional[SimpleSpec]: # type: ignore[no-any-unimp
def entry_points(self) -> List[str]:
entry_points = []
if self.script and self.binding == Binding.Exec:
for name, mod in self.target.items():
for executable, mod in self.target.items():
base_mod, name = mod.rsplit(".")
script = "%s=%s.%s:run" % (name, base_mod, "_gen_%s" % name)
script = "%s=%s.%s:run" % (name, base_mod, _script_name(executable))
entry_points.append(script)

return entry_points

def install_script(self, module_name: str, exe_path: str) -> None:
if self.script and self.binding == Binding.Exec:
dirname, executable = os.path.split(exe_path)
file = os.path.join(dirname, "_gen_%s.py" % module_name)
script_name = _script_name(executable)
file = os.path.join(dirname, f"{script_name}.py")
with open(file, "w") as f:
f.write(_TMPL.format(executable=repr(executable)))
f.write(_SCRIPT_TEMPLATE.format(executable=repr(executable)))

def _metadata(self) -> "_CargoMetadata":
"""Returns cargo metedata for this extension package.
Expand All @@ -207,7 +212,26 @@ def _uses_exec_binding(self) -> bool:
_CargoMetadata = NewType("_CargoMetadata", Dict[str, Any])


_TMPL = """
def _script_name(executable: str) -> str:
"""Generates the name of the installed Python script for an executable.
Because Python modules must be snake_case, this generated script name will
replace `-` with `_`.
>>> _script_name("hello-world")
'_gen_hello_world'
>>> _script_name("foo_bar")
'_gen_foo_bar'
>>> _script_name("_gen_foo_bar")
'_gen__gen_foo_bar'
"""
script = executable.replace("-", "_")
return f"_gen_{script}"


_SCRIPT_TEMPLATE = """
import os
import sys
Expand Down

0 comments on commit 458544f

Please sign in to comment.