Skip to content

Commit

Permalink
Merge pull request #5390 from jerempy/Scripts-run-package-functions
Browse files Browse the repository at this point in the history
Scripts run package functions
  • Loading branch information
oz123 committed Oct 9, 2022
2 parents ba7ec03 + 5f548d0 commit 612241b
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 2 deletions.
1 change: 1 addition & 0 deletions news/5294.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ability for callable scripts in Pipfile under [scripts]. Callables can now be added like: ``<pathed.module>:<func>`` and can also take arguments. For exaple: ``func = {call = "package.module:func('arg1', 'arg2')"}`` then this can be activated in the shell with ``pipenv run func``
32 changes: 31 additions & 1 deletion pipenv/cmdparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,61 @@
import re
import shlex

from pipenv.vendor 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]
if args:
self._parts.extend(args)

@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)
Expand Down
31 changes: 31 additions & 0 deletions tests/integration/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from pipenv.project import Project
from pipenv.utils.shell import subprocess_run, temp_environ
from pipenv.utils.shell import mkdir_p


@pytest.mark.run
Expand Down Expand Up @@ -63,6 +64,36 @@ def test_scripts(pipenv_instance_pypi):
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):
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_vendor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import pytest
import pytz
import tomlkit
from pipenv.vendor import tomlkit


@pytest.mark.parametrize('dt, content', [
Expand Down

0 comments on commit 612241b

Please sign in to comment.