Hosted by:

# Special

There is only one instance of this 'special' knowledge base, called special.

The special knowledge base is a collection of miscellaneous helper knowledge entities that determine whether a statement is true or not in various interesting ways.

Thus, each entity in this knowledge base is a Python function that does something "special" when run.

The special functions are:

## Claim_goal

The claim_goal function has no arguments:

special.claim_goal()

This acts like the Prolog cut operator.

In general there are multiple rules that might be used to try to prove any goal. They are each tried in the order that they appear in the .krb file. If one rule fails, the next rule is tried. The goal itself doesn't fail until all of the rules for it have failed.

### Example

Suppose I want to translate a number, N, into the phrase "N dogs". I could use the following rules:

one_dog
use n_dogs(1, '1 dog')

n_dogs
use n_dogs(\$n, \$phrase)
when
\$phrase = "%d dogs" % \$n

The problem here is that both rules might be used when n is 1, but the second rule isn't appropriate in this case. Special.claim_goal() may be used to fix this, as follows:

one_dog
use n_dogs(1, '1 dog')
when
special.claim_goal()

n_dogs
use n_dogs(\$n, \$phrase)
when
\$phrase = "%d dogs" % \$n

The special.claim_goal() prevents the second rule from being used when n is 1.

### Explanation

When a rule executes special.claim_goal() in its when clause, none of the rest of the rules will be tried for that goal. Thus, when special.claim_goal() is backtracked over, the goal fails immediately without trying any more rules for it.

This ends up acting like an "else". You place it in the when clause after the premises that show that this rule must be the correct one to use. Then the subsequent rules will only be tried if these premises fail, such that special.claim_goal() is never executed.

This means that you don't need to add extra premises in each subsequent rule to make sure that these premises have not occurred.

Without the special.claim_goal() in the prior example, you would have to write:

one_dog
use n_dogs(1, '1 dog')

n_dogs
use n_dogs(\$n, \$phrase)
when
check \$n != 1
\$phrase = "%d dogs" % \$n

This is a simple example where it is easy to add the check in the second rule. But in general, it can be difficult to check for prior conditions, especially when many rules are involved that each has its own condition.

## Running Commands

The remaining three functions deal with running programs (commands) on the host computer that is running your Pyke program. Their difference is in what kind of output they provide from the command.

These functions all use the subprocess.Popen function from the standard Python library.

Thus, each of these functions accept these three parameters that are passed on to subprocess.Popen:

• The \$command parameter (required).
• This is a tuple indicating the program to run along with its command line arguments, such as (ls, '-l').
• The \$cwd parameter (optional).
• This specifies the current working directory to run the command in.
• If omitted or None the current working directory is not changed.
• The \$stdin parameter (optional).
• This is a string that is fed to the command as its stdin.
• If the command expects multiple lines of input, this string must include embedded newlines (e.g., 'line 1\nline 2\n').
• If omitted or None, no stdin is provided to the command.

All of these functions fail on backtracking.

### Check_command

special.check_command(\$command [, \$cwd [, \$stdin]])

Succeeds if the command returns a zero exit status. Fails otherwise. Any output from the command to stdout or stderr is unavailable.

>>> from pyke import knowledge_engine
>>> engine = knowledge_engine.engine()
>>> engine.prove_1('special', 'check_command', (('true',),), 0)
((), None)
>>> engine.prove_1('special', 'check_command', (('false',),), 0)
Traceback (most recent call last):
...
pyke.knowledge_engine.CanNotProve: Can not prove special.check_command((false))

### Command

special.command(\$stdout, \$command [, \$cwd [, \$stdin]])

This just looks at the stdout of the command. Any output from the command to stderr is unavailable.

The \$stdout is a tuple of lines with the trailing newlines removed.

This raises subprocess.CalledProcessError if the command returns a non-zero exit status.

>>> from __future__ import with_statement
>>> from pyke import pattern, contexts
>>> def run_command(entity, command, cwd=None, stdin=None):
...     my_context = contexts.simple_context()
...     output = contexts.variable('output')
...     with engine.prove('special', entity, my_context,
...                       (output,
...                        pattern.pattern_literal(command),
...                        pattern.pattern_literal(cwd),
...                        pattern.pattern_literal(stdin))) \
...       as gen:
...         for no_plan in gen:
...             print(output.as_data(my_context))
>>> run_command('command', ('echo', 'hi', 'mom'))
('hi mom',)
>>> run_command('command', ('ls',))   # doctest: +NORMALIZE_WHITESPACE
('ast_syntax', 'cheatsheets', 'examples', 'html', '__init__.py',
'__init__.pyc', 'PyKE.mm', 'r2w.ini', 'source', 'syntax', 'testdocs',
'todo')
>>> run_command('command', ('ls', '-l', 'r2w.ini')) # doctest: +ELLIPSIS
('-rw-r--r-- 1 ... r2w.ini',)
>>> run_command('command', ('tail', '-n', '5', 'template.txt', '-'),
...             'source',   # cwd
...             'stdin: line 1\nstdin: line 2\nstdin: line 3\n')
...     # doctest: +NORMALIZE_WHITESPACE
('==> template.txt <==',
'   } catch(err) {}',
'  </script>',
'</body>',
'</html>',
'',
'',
'==> standard input <==',
'stdin: line 1',
'stdin: line 2',
'stdin: line 3')
>>> run_command('command', ('false',))
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'false' returned non-zero exit status 1

### General_command

special.general_command(\$output, \$command [, \$cwd [, \$stdin]])

This is the fully general form that gives you all output from the command.

The \$output is a three tuple: (exit_status, stdout, stderr). Both stdout and stderr are single strings (with embedded newlines).

>>> run_command('general_command', ('echo', 'hi', 'mom'))
(0, 'hi mom\n', '')
>>> run_command('general_command', ('cat', 'foobar'))
(1, '', 'cat: foobar: No such file or directory\n')
>>> run_command('general_command', ('tail', '-n', '5', 'r2w.ini', 'foobar'))
...     # doctest: +NORMALIZE_WHITESPACE
(1,
"==> r2w.ini <==\ntarget_directory = 'html'\nmacros =
''\n\n[uservalues]\nversion = '0.2'\n",
"tail: cannot open `foobar' for reading: No such file or directory\n")

### More:

Fact Bases

Explanation of facts and fact bases.

Rule Bases

Explanation of rule bases, overview of .krb files and how these files are compiled and loaded into your Python program.

Question Bases

Explanation of question bases and .kqb files.

Special

Explanation of the special knowledge base.