Skip to content
Open
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
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ def echo(*args,**kwargs):
print args
print kwargs

# You can optionally enforce strict task name matching; if left out, the first
# few characters of the task name are enough to execute the task, as long as
# the partial name is unambigious
# __TASK_NAME_RESOLVER__ = 'strict'

# Default task (if specified) is run when no task is specified in the command line
# make sure you define the variable __DEFAULT__ after the task is defined
# A good convention is to define it at the end of the module
Expand Down Expand Up @@ -151,7 +156,9 @@ Starting server at localhost:80
[ example.py - Completed task "start_server" ]
```

The first few characters of the task name is enough to execute the task, as long as the partial name is unambigious. You can specify multiple tasks to run in the commandline. Again the dependencies are taken taken care of.
The first few characters of the task name are enough to execute the task, as long as the partial name is unambigious. This behaviour can be disabled by adding the statement `__TASK_NAME_RESOLVER__ = 'strict'` to your build file.

You can specify multiple tasks to run in the commandline. Again the dependencies are taken taken care of.

```bash
$ pynt cle ht cl
Expand Down
16 changes: 12 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ still be run with ``pynt _private_task_name``
print args
print kwargs

# You can optionally enforce strict task name matching; if left out, the first
# few characters of the task name are enough to execute the task, as long as
# the partial name is unambigious
# __TASK_NAME_RESOLVER__ = 'strict'

# Default task (if specified) is run when no task is specified in the command line
# make sure you define the variable __DEFAULT__ after the task is defined
# A good convention is to define it at the end of the module
Expand Down Expand Up @@ -160,10 +165,13 @@ ignored).
Starting server at localhost:80
[ example.py - Completed task "start_server" ]

The first few characters of the task name is enough to execute the task,
as long as the partial name is unambigious. You can specify multiple
tasks to run in the commandline. Again the dependencies are taken taken
care of.
The first few characters of the task name are enough to execute the task,
as long as the partial name is unambigious. This behaviour can be disabled
by adding the statement `__TASK_NAME_RESOLVER__ = 'strict'` to your build
file.

You can specify multiple tasks to run in the commandline. Again the dependencies
are taken taken care of.

::

Expand Down
2 changes: 1 addition & 1 deletion pynt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Lightweight Python Build Tool
"""

__version__ = "0.8.2"
__version__ = "0.8.3"
__license__ = "MIT License"
__contact__ = "http://rags.github.com/pynt/"
from ._pynt import task, main
Expand Down
29 changes: 18 additions & 11 deletions pynt/_pynt.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ def _load_buildscript(file_path):
with open(file_path, 'r') as script_file:
return imp.load_module(module_name, script_file, file_path, description)

def _match_task_names_heuristically(module) -> bool:
for name, value in inspect.getmembers(module, lambda v: isinstance(v, str)):
if name == '__TASK_NAME_RESOLVER__':
return value.strip().lower() != 'strict'
return True

def _get_default_task(module):
matching_tasks = [task for name,task in inspect.getmembers(module,Task.is_task)
if name == "__DEFAULT__"]
Expand Down Expand Up @@ -130,17 +136,18 @@ def _get_task(module, name, tasks):
args, kwargs= _parse_args(args_str)
if hasattr(module, task_name):
return getattr(module, task_name), args, kwargs
matching_tasks = [task for task in tasks if task.name.startswith(task_name)]

if not matching_tasks:
raise Exception("Invalid task '%s'. Task should be one of %s" %
(name,
', '.join([task.name for task in tasks])))
if len(matching_tasks) == 1:
return matching_tasks[0], args, kwargs
raise Exception("Conflicting matches %s for task %s" % (
', '.join([task.name for task in matching_tasks]), task_name
))

if _match_task_names_heuristically(module):
matching_tasks = [task for task in tasks if task.name.startswith(task_name)]
if len(matching_tasks) == 1:
return matching_tasks[0], args, kwargs
elif len(matching_tasks) > 1:
raise Exception("Conflicting matches %s for task %s" % (
', '.join([task.name for task in matching_tasks]), task_name
))
raise Exception("Invalid task '%s'. Task should be one of %s" %
(name,
', '.join([task.name for task in tasks])))

def _parse_args(args_str):
args = []
Expand Down
50 changes: 50 additions & 0 deletions pynt/tests/build_scripts/build_with_strict_task_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/python

from pynt import task

tasks_run = []

@task()
def clean(directory='/tmp'):
tasks_run.append('clean[%s]' % directory)


@task(clean)
def html():
tasks_run.append('html')


@task()
def tests(*test_names):
tasks_run.append('tests[%s]' % ','.join(test_names))


@task(clean)
def copy_file(from_, to, fail_on_error='True'):
tasks_run.append('copy_file[%s,%s,%s]' % (from_, to, fail_on_error))


@task(clean)
def start_server(port='80', debug='True'):
tasks_run.append('start_server[%s,%s]' % (port, debug))

@task(ignore=True)
def ignored(file, contents):
tasks_run.append('append_to_file[%s,%s]' % (file, contents))

@task(clean, ignored)
def append_to_file(file, contents):
tasks_run.append('append_to_file[%s,%s]' % (file, contents))


@task(ignored)
def echo(*args,**kwargs):
args_str = []
if args:
args_str.append(','.join(args))
if kwargs:
args_str.append(','.join("%s=%s" % (kw, kwargs[kw]) for kw in sorted(kwargs)))

tasks_run.append('echo[%s]' % ','.join(args_str))

__TASK_NAME_RESOLVER__ = 'strict'
18 changes: 18 additions & 0 deletions pynt/tests/test_pynt.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,24 @@ def test_exception_on_conflicting_partial_names(self):
'Conflicting matches copy_file, clean for task c' in str(exc.value))


class TestStrictTaskNames:

def setup_method(self):
from .build_scripts import build_with_strict_task_names
self._mod = build_with_strict_task_names

def test_task_resolver_statement(self):
assert False == _pynt._match_task_names_heuristically(self._mod)

def test_exception_on_partial_task_name(self):
with pytest.raises(Exception) as exc:
build(self._mod, ["c"])
assert 'Invalid task \'c\'' in str(exc.value)

def test_with_valid_task_name_and_dependencies(self):
mod = build(self._mod, ["html"])
assert ['clean[/tmp]','html'] == mod.tasks_run


class TestDefaultTask:
def test_simple_default_task(self):
Expand Down