@@ -58,10 +58,14 @@ class RustExtension:
58
58
packages, i.e *not* a filename or pathname. It is possible to
59
59
specify multiple binaries, if extension uses ``Binding.Exec``
60
60
binding mode. In that case first argument has to be dictionary.
61
- Keys of the dictionary corresponds to compiled rust binaries and
62
- values are full name of the executable inside python package.
61
+ Keys of the dictionary correspond to the rust binary names and
62
+ values are the full dotted name to place the executable inside
63
+ the python package. To install executables with kebab-case names,
64
+ the final part of the dotted name can be in kebab-case. For
65
+ example, `hello_world.hello-world` will install an executable
66
+ named `hello-world`.
63
67
path: Path to the ``Cargo.toml`` manifest file.
64
- args: A list of extra argumenents to be passed to Cargo. For example,
68
+ args: A list of extra arguments to be passed to Cargo. For example,
65
69
``args=["--no-default-features"]`` will disable the default
66
70
features listed in ``Cargo.toml``.
67
71
features: A list of Cargo features to also build.
@@ -169,19 +173,20 @@ def get_rust_version(self) -> Optional[SimpleSpec]: # type: ignore[no-any-unimp
169
173
def entry_points (self ) -> List [str ]:
170
174
entry_points = []
171
175
if self .script and self .binding == Binding .Exec :
172
- for name , mod in self .target .items ():
176
+ for executable , mod in self .target .items ():
173
177
base_mod , name = mod .rsplit ("." )
174
- script = "%s=%s.%s:run" % (name , base_mod , "_gen_%s" % name )
178
+ script = "%s=%s.%s:run" % (name , base_mod , _script_name ( executable ) )
175
179
entry_points .append (script )
176
180
177
181
return entry_points
178
182
179
183
def install_script (self , module_name : str , exe_path : str ) -> None :
180
184
if self .script and self .binding == Binding .Exec :
181
185
dirname , executable = os .path .split (exe_path )
182
- file = os .path .join (dirname , "_gen_%s.py" % module_name )
186
+ script_name = _script_name (module_name )
187
+ file = os .path .join (dirname , f"{ script_name } .py" )
183
188
with open (file , "w" ) as f :
184
- f .write (_TMPL .format (executable = repr (executable )))
189
+ f .write (_SCRIPT_TEMPLATE .format (executable = repr (executable )))
185
190
186
191
def _metadata (self ) -> "_CargoMetadata" :
187
192
"""Returns cargo metedata for this extension package.
@@ -207,7 +212,26 @@ def _uses_exec_binding(self) -> bool:
207
212
_CargoMetadata = NewType ("_CargoMetadata" , Dict [str , Any ])
208
213
209
214
210
- _TMPL = """
215
+ def _script_name (executable : str ) -> str :
216
+ """Generates the name of the installed Python script for an executable.
217
+
218
+ Because Python modules must be snake_case, this generated script name will
219
+ replace `-` with `_`.
220
+
221
+ >>> _script_name("hello-world")
222
+ '_gen_hello_world'
223
+
224
+ >>> _script_name("foo_bar")
225
+ '_gen_foo_bar'
226
+
227
+ >>> _script_name("_gen_foo_bar")
228
+ '_gen__gen_foo_bar'
229
+ """
230
+ script = executable .replace ("-" , "_" )
231
+ return f"_gen_{ script } "
232
+
233
+
234
+ _SCRIPT_TEMPLATE = """
211
235
import os
212
236
import sys
213
237
0 commit comments