added callables to [scripts] in Pipfile

Can now add a callable like {call = "package.module:func('arg')"}
This commit is contained in:
jerempy
2022-10-06 13:05:39 -04:00
parent ba7ec0313a
commit bb0944265b
2 changed files with 59 additions and 1 deletions
+28 -1
View File
@@ -1,23 +1,47 @@
import itertools
import re
import shlex
import tomlkit
class ScriptEmptyError(ValueError):
pass
class ScriptParseError(ValueError):
pass
def _quote_if_contains(value, pattern):
if next(iter(re.finditer(pattern, value)), None):
return '"{0}"'.format(re.sub(r'(\\*)"', r'\1\1\\"', value))
return value
def _parse_toml_inline_table(value: tomlkit.items.InlineTable) -> str:
"""parses the [scripts] in pipfile and converts: `{call = "package.module:func('arg')"}` into an executable command
"""
keys_list = list(value.keys())
if len(keys_list) > 1:
raise ScriptParseError("More than 1 key in toml script line")
cmd_key = keys_list[0]
if cmd_key not in Script.script_types:
raise ScriptParseError(f"Not an accepted script callabale, options are: {Script.script_types}")
if cmd_key == "call":
module, _, func = str(value["call"]).partition(":")
if not module or not func:
raise ScriptParseError("Callable must be like: <pathed.module>:<func>")
if re.search(r"\(.*?\)", func) is None:
func += "()"
return f"python -c \"import {module} as _m; _m.{func}\""
class Script(object):
"""Parse a script line (in Pipfile's [scripts] section).
This always works in POSIX mode, even on Windows.
"""
script_types = ["call"]
def __init__(self, command, args=None):
self._parts = [command]
@@ -26,7 +50,10 @@ class Script(object):
@classmethod
def parse(cls, value):
if isinstance(value, str):
if isinstance(value, tomlkit.items.InlineTable):
cmd_string = _parse_toml_inline_table(value)
value = shlex.split(cmd_string)
elif isinstance(value, str):
value = shlex.split(value)
if not value:
raise ScriptEmptyError(value)
+31
View File
@@ -4,6 +4,7 @@ import pytest
from pipenv.project import Project
from pipenv.utils.shell import subprocess_run, temp_environ
from pipenv.utils.shell import mkdir_p
@pytest.mark.run
@@ -63,6 +64,36 @@ multicommand = "bash -c \"cd docs && make html\""
assert c.stdout.strip() == "WORLD"
@pytest.mark.run
def test_scripts_with_package_functions(pipenv_instance_pypi):
with pipenv_instance_pypi(chdir=True) as p:
p.pipenv('install')
pkg_path = os.path.join(p.path, "pkg")
mkdir_p(pkg_path)
file_path = os.path.join(pkg_path, "mod.py")
with open(file_path, "w+") as f:
f.write("""
def test_func():
print("success")
def arg_func(s, i):
print(f"{s.upper()}. Easy as {i}")
""")
with open(p.pipfile_path, 'w') as f:
f.write(r"""
[scripts]
pkgfunc = {call = "pkg.mod:test_func"}
argfunc = {call = "pkg.mod:arg_func('abc', 123)"}
""")
c = p.pipenv('run pkgfunc')
assert c.stdout.strip() == "success"
c = p.pipenv('run argfunc')
assert c.stdout.strip() == "ABC. Easy as 123"
@pytest.mark.run
@pytest.mark.skip_windows
def test_run_with_usr_env_shebang(pipenv_instance_pypi):