The following is a list of questions that have been asked by PyCLIPS users. Its contents are almost the same as the FAQ file supplied with the source distribution. Optionally there can be comments and updates that in some cases may provide a better explanation.
There is actually no way to inspect the "history" of commands that have been sent to the underlying CLIPS engine. In fact, unlike similar projects, PyCLIPS does not limit its interaction with CLIPS to sending commands to the engine. What PyCLIPS mostly does is to call the intrinsic low-level CLIPS API to access and modify the engine state. However the underlying engine keeps its ability to produce a possibly (even too densely) detailed trace file. See the documentation for the DebugConfig object in the manual in order to learn how to produce a trace file with the desired detail, and the CLIPS manual if you are uncertain about what entities or actions can be "watched" and for what purpose.
When the Run() function is invoked without arguments, it just returns control to the calling Python program when it has finished running. In most cases this is sufficient, but sometimes it might be useful to divide execution in steps, eg. when the CLIPS subprogram is known to take a long time to finish and there is the need to give some feedback to the user. In this case you can just decide to perform a certain number of steps repeatedly until no rules are fired anymore, as the Run command accepts a "limit" integer argument which must be the maximum number of rules to execute. An example follows:
STEPS = 5 import clips define_some_rules() clips.Reset() assert_some_facts() while not clips.Run(STEPS) < STEPS: give_some_feedback() possibly_consider_activations() print_result()
Run() returns the number of fired rules, so it eventually becomes less than expected (zero if no rules are fired anymore) and this means that the CLIPS subprogram has finished. At any step the calling program can take into consideration the newly activated rules, giving also more granularity to the process.
Salience declaration is part of the LHS of the rule, so it is legal to declare salience in the LHS. The implementation of BuildRule() relies on the more general Build() function: the construct is converted to a string and then sent to the CLIPS engine via the same low-level call that Build() invokes, while immediately looking up for the newly created rule and returning it as an object. Unfortunately there is currently no way to set the salience of a rule after building it (eg. as a property of the returned Rule object). An example follows:
r = clips.BuildRule("duck-rule", """(declare (salience 99)) (duck) """, "(assert (quack))")
Of course PyCLIPS does not rely on multi-line strings and spacing, as its main delimiter is the bracket. The above example uses multiple lines only for sake of readability.
There is actually no ready-made Activation list. The documentation states that Activation objects cannot be created by the user, and can only be retrieved from the system. In fact it would make no sense to create an Activation object, as it occurs only when, before (or within) a Run(), a certain rule is likely to be fired. Also, being Activations mainly a sort of "temporary" objects, they have no name... hence the absence of a "list of activations" (the List() functions, with few exceptions, just provide lists of *names*). However, the possibility to walk through the list of current activations is provided by the "iteration functions":
InitialActivation() (returns the first activation as an *object*)
Activation.Next() (returns the activation that follows as an object).
There are several ways, actually. One can be directly reading what the engine reports on TraceStream and ErrorStream, by explicitly using their Read() method. The easiest way I have found (it's time consuming, though) is to clips.DebugConfig.WatchAll() to activate all trace output and clips.DebugConfig.DribbleOn("my_trace.log"). When an error occurs in CLIPS, everything about it will be written to the file specified in DebugConfig.DribbleOn(), and it becomes easier to spot it correctly.
It might be a bug, of course. But sometimes such differences depend on the implementation of PyCLIPS as a layer between Python and the API that CLIPS exposes to the C language. PyCLIPS does not necessarily behave like the CLIPS shell itself, however it's possible to directly interact with the underlying CLIPS engine in a very similar way using the SendCommand() function. There is a limitation for SendCommand(), though: it only examines the first part of the command, that is either the first symbol or the first expression enclosed in the outermost brackets. This means that issuing a command like:
clips.SendCommand("(assert (spam)) and egg")
will only send the command "(assert (spam))" to CLIPS, and forget the rest. But SendCommand() closely mimics the behaviour of the CLIPS shell, and if you try to supply the same command to CLIPS, you will notice exactly the same reaction.
The module has not been created for this task. However, there are all the commands and utility functions (namely SendCommand and I/O "streams") to allow creating a simple shell. Probably I will post the script I use as a CLIPS shell from within the Python interactive interpreter. Anyway it should be quite easy to create a small script that, in an "endless" loop, does something like:
while True: s = read_user_input() clips.SendCommand(s) o = clips.StdoutStream.Read() e = clips.ErrorStream.Read() sys.stdout.write(o) sys.stderr.write(e)
possibly enhancing it to read more information from other streams and to perform some more tests on user input.
Yes, definitely. The underlying CLIPS engine has to be treated as a single resource - although it can handle multiple separate environments - thus "real" concurrent access is not possible. Multiple threads can use the resource, but they are implicitly blocked by the Python interpreter itself when they try to access the PyCLIPS low-level layer. As a side effect, also the Run() function could cause a Python program to release control to the interpreter after a long time. It wouldn't be safe to reimplement PyCLIPS in a way that allows the Python interpreter to access the engine while a time-consuming function (such as the Run() function) is executing, as CLIPS has not been designed for multithreading. However if your problem is the lack of responsiveness specifically during a Run() you can refer to the second "Answered Question" above for a *partial* solution.
This is very common, it happened to me too. I thought I was smart but forgot to install the Python development packages on my Linux machine. Normally these packages are named like "python-devel" or "python-dev", and are absolutely necessary to build Python modules and extensions. One possible reason can also be that you are using an embedded version of Python: in this case it would be better to install a standalone Python interpreter, same version as the embedded one, and use it to build the module. After doing that, you should copy the 'clips' subdirectory from its 'site-packages' to your embedded Python 'site-packages': this should, in most cases, allow you to call PyCLIPS from an embedded Python release. I don't know how to link PyCLIPS with Python in a custom build: probably it's quite easy... if anyone finds out how to do it, please give me some information about it.
Yes, but you can't use clips.StdinStream and clips.StdoutStream in an effective way. Ok, if you structure your CLIPS subprogram very carefully and pay special attention to the execution order you might also be able to use the default CLIPS streams to provide input from an hypothetical user, but the quickest way to achieve a result is to let Python do the I/O job using a Python function:
def clips_raw_input(prompt): return clips.String(raw_input(prompt)) clips.RegisterPythonFunction(clips_raw_input, "raw-input")
this function will let Python interact with the user, such as in the following session:
>>> import clips >>> def clips_raw_input(prompt): return clips.String(raw_input(prompt)) >>> clips.RegisterPythonFunction(clips_raw_input, "raw-input") >>> r1 = clips.BuildRule( "name-rule", "(initial-fact)", """(bind ?user-name (python-call raw-input "Your Name? ")) (assert (user-name-is ?user-name))""") >>> clips.Reset() >>> clips.Run() Your Name? Francesco 1 >>> clips.PrintFacts() f-0 (initial-fact) f-1 (user-name-is "Francesco") For a total of 2 facts. >>>
Of course you can use raw_input, but you can also pop up a dialog box or whatever else to retrieve input from the interactive session, using virtually all Python possibilities.