{ "info": { "author": "Michael Joseph Walsh", "author_email": "github.com{nospam}nemonik.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "*My apologies, a better readme will follow.*\n\nIntellect\n=========\n\n:Info: Intellect is a Domain-specific language and Rules Engine for Python.\n\n:Author: Michael Joseph Walsh\n\n1. What is Intellect\n--------------------\n\nIntellect is a DSL (\"Domain-Specific Language\") and Rule Engine for Python\nI authored for expressing policies to orchestrate and control a dynamic\nnetwork defense cyber-security platform being researched in The \nMITRE Corporation's Innovation Program. \n\nThe rules engine provides an intellect, a form of artificial intelligence,\na faculty of reasoning and understanding objectively over a working memory. \nThe memory retains knowledge relevant to the system, and a set of rules\nauthored in the DSL that describe a necessary behavior to achieve some\ngoal. Each rule has an optional condition, and a suite of one or more\nactions. These actions either further direct the behavior of the system,\nand/or further inform the system. The engine starts with some facts,\ntruths known about past or present circumstances, and uses rules to infer\nmore facts. These facts fire more rules, that infer more facts and so\non.\n\nFor the platform in the Innovation Program, the network defender uses\nthe DSL to confer policy, how the platform is to respond to network\nevents mounted over covert network channels, but there are no direct\nties written into the language nor the rule engine to cyber security\nand thus the system in its entirety can be more broadly used in\nother domains.\n\n2. Installation\n---------------\n\n* To install via `setuptools `_ use ``easy_install -U Intellect``\n* To install via `pip `_ use ``pip install Intellect``\n* To install via `pypm `_ use ``pypm install intellect``\n* Or download the latest source from `Master `_ or the most recent tagged release `Tagged Releases `_, unpack, and run ``python setup.py install`` \n\n\n3. Dependencies\n---------------\n\n* `ANTLR3 Python Runtime 3.1.3 `_\n* Python itself, if you don't already have it. I tested the code on Python 2.7.1, 2.7.2, 2.7.3. \n\n4. Contribution\n---------------\n\nThe source code is available under the BSD 4-clause license. If you have ideas, \ncode, bug reports, or fixes you would like to contribute please do so.\n\nBugs and feature requests can be filed at `Github `_.\n\n5. Background\n-------------\n\nMany production rule system implementations have been open-sourced, such as\nJBoss Drools, Rools, Jess, Lisa, et cetera. If you're familiar with the \nDrools syntax, Intellect's syntax should look familiar. (I'm not saying it \nis based on it, because it is not entirely, but I found as I was working\nthe syntax I would check with Drools and if made sense to push in the \ndirection of Drools, this is what I did.) The aforementioned implementations\nare available for other languages for expressing production rules, but it is \nmy belief that Python is under-represented, and as such it was my thought the\nlanguage and rule engine could benefit from being open sourced, and so I put\na request in. \n\nThe MITRE Corporation granted release August 4, 2011.\n\nThus, releasing the domain-specific language (DSL) and Rule Engine to Open\nSource in the hopes doing so will extend its use and increase its chances \nfor possible adoption, while at the same time mature the project with more \ninterested eyeballs being placed on it.\n\nStarting out, it was initially assumed the aforementioned platform would \nbe integrated with the best Open Source rules engine available for \nPython as there are countless implementation for Ruby, Java, and Perl, \nbut surprisingly I found none fitting the project's needs. This led to \nthe thought of inventing one; simply typing the keywords \"python rules \nengine\" into Google though will return to you the advice \"to not invent \nyet another rules language\", but instead you are advised to \"just write \nyour rules in Python, import them, and execute them.\" The basis for this \nadvice can be coalesced down to doing so otherwise does not fit with the \n\"Python Philosophy.\" At the time, I did not believe this to be true, nor \nfully contextualized, and yet admittedly, I had not yet authored a line \nof Python code (Yes, you're looking at my first Python program. So,\nplease give me a break.) nor used ANTLR3 prior to this effort. Looking \nback, I firmly believe the act of inventing a rules engine and abstracting it \nbehind a nomenclature that describes and illuminates a specific domain is \nthe best way for in case of aforementioned platform the network defender \nto think about the problem. Like I said though the DSL and rules engine\ncould be used for anything needing a \"production rule system\".\n\nAs there were no rules engines available for Python fitting the platforms\nneeds, a policy language and naive forward chaining rules engine were built \nfrom scratch. The policy language's grammar is based on a subset of Python \nlanguage syntax. The policy DSL is parsed and lexed with the help of the \nANTLR3 Parse Generator and Runtime for Python. \n\n\n6. Facts (Data being reasoned over)\n-----------------------------------\n\nThe interpreter, the rules engine, and the remainder of the code such as \nobjects for conferring discrete network conditions, referred to as \"facts\",\nare also authored in Python. Python's approach to the object-oriented programming\nparadigm, where objects consist of data fields and methods, did not easily\nlend itself to describing \"facts\". Because the data fields of a Python object \nreferred to syntactically as \"attributes\" can and often are set on an \ninstance of a class, they will not exist prior to a class's instantiation. \nIn order for a rules engine to work, it must be able to fully introspect an \nobject instance representing a condition. This proves to be very difficult \nunless the property decorator with its two attributes, \"getter\" and \"setter\", \nintroduced in Python 2.6, are adopted and formally used for authoring these objects. \nCoincidentally, the use of the \"Getter/Setter Pattern\" used frequently in \nJava is singularly frowned upon in the Python developer community with the \ncheer of \"Python is not Java\".\n\nSo, you will need to author your facts as Python object's who attributes \nare formally denoted as properties like so for the attributes you would like to\nreason over::\n\n\tclass ClassA(object):\n\t\t'''\n\t\tAn example fact\n\t\t'''\n\t\n\t\tdef __init__(self, property0 = None, property1 = None):\n\t\t\t'''\n\t\t\tClassA initializer\n\t\t\t'''\n\t\t\tself._property0 = property0\n\t\n\t\t@property\n\t\tdef property0(self):\n\t\t\treturn self._property0\n\t\n\t\t@property0.setter\n\t\tdef property0(self, value):\n\t\t\tself._property0 = value\n\n7. The Policy DSL\n-----------------\n\nExample with policy files can be found at the path `Intellect/src/intellect/examples `_. \nPolicy files must follow the Policy grammar as define in `Intellect/src/intellect/grammar/Policy.g `_. \nThe rest of this section documents the grammar of policy domain-specific language.\n\n7.1 Import Statements (``ImportStmts``)\n---------------------------------------\n\nImport statements basically follow Python's with a few limitations. For\nexample, The wild card form of import is not supported for the reasons\nelaborated `here `_\nand follow the Python 2.7.2 grammar. ``ImportStmt`` statements exist only at the same\nlevel of ``ruleStmt`` statements as per the grammar, and are typically at the top of a\npolicy file, but are not limited to. In fact, if you break up your policy across several \nfiles the last imported as class or module wins as the one being named.\n\n.. _7.2:\n\n7.2 Attribute Statements (``attribute``)\n----------------------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/attributeStmt.jpg\n \n The syntax diagram for a ``attributeStmt``.\n\n``attributeStmt`` statements are expressions used to create policy attributes, a form of\nglobals, that are accessible from rules.\n\nFor example, a policy could be written::\n\n\timport logging\n\t\n\tfirst_sum = 0\n\tsecond_sum = 0\n\t\n\trule \"set both first_sum and second_sum to 1\":\n\t\tagenda-group \"test_d\"\n\t\tthen:\n\t\t\tattribute (first_sum, second_sum) = (1,1)\n\t\t\tlog(\"first_sum is {0}\".format(first_sum), \"example\", logging.DEBUG)\n\t\t\tlog(\"second_sum is {0}\".format(second_sum), \"example\", logging.DEBUG)\n\t\n\trule \"add 2\":\n\t\tagenda-group \"test_d\"\n\t\tthen:\n\t\t\tattribute first_sum += 2\n\t\t\tattribute second_sum += 2\n\t\t\tlog(\"first_sum is {0}\".format(first_sum), \"example\", logging.DEBUG)\n\t\t\tlog(\"second_sum is {0}\".format(second_sum), \"example\", logging.DEBUG)\n\t\n\trule \"add 3\":\n\t\tagenda-group \"test_d\"\n\t\tthen:\n\t\t\tattribute first_sum += 3\n\t\t\tattribute second_sum += 3\n\t\t\tlog(\"first_sum is {0}\".format(first_sum), \"example\", logging.DEBUG)\n\t\t\tlog(\"second_sum is {0}\".format(second_sum), \"example\", logging.DEBUG)\n\t\n\trule \"add 4\":\n\t\tagenda-group \"test_d\"\n\t\tthen:\n\t\t\tattribute first_sum += 4\n\t\t\tattribute second_sum += 4\n\t\t\tlog(\"first_sum is {0}\".format(first_sum), \"example\", logging.DEBUG)\n\t\t\tlog(\"second_sum is {0}\".format(second_sum), \"example\", logging.DEBUG)\n\t\t\thalt\n\t\n\trule \"should never get here\":\n\t\tagenda-group \"test_d\"\n\t\tthen:\n\t\t\tlog(\"Then how did I get here?\", \"example\", logging.DEBUG)\n\ncontaining the two ``atributeStmt`` statements::\n\n\tfirst_sum = 0\n\tsecond_sum = 0 \n\nThe following rules will increment these two attributes using ``attributeAction``\nstatements.\n\nCode to exercise this policy would look like so::\n\n\tclass MyIntellect(Intellect):\n\t\tpass\n\t\n\tif __name__ == \"__main__\":\n\t\n\t\t# set up logging for the example\n\t\tlogger = logging.getLogger('example')\n\t\tlogger.setLevel(logging.DEBUG)\n\t\n\t\tconsoleHandler = logging.StreamHandler(stream=sys.stdout)\n\t\tconsoleHandler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s%(message)s'))\n\t\tlogger.addHandler(consoleHandler)\n\t\n\t\tmyIntellect = MyIntellect()\n\t\n\t\tpolicy_d = myIntellect.learn(Intellect.local_file_uri(\"./rulesets/test_d.policy\"))\n\t\n\t\tmyIntellect.reason([\"test_d\"])\n\nand the logging output from the execution of the above would be::\n\n\t2011-10-04 23:56:51,681 example DEBUG __main__.MyIntellect :: first_sum is 1\n\t2011-10-04 23:56:51,682 example DEBUG __main__.MyIntellect :: second_sum is 1\n\t2011-10-04 23:56:51,683 example DEBUG __main__.MyIntellect :: first_sum is 3\n\t2011-10-04 23:56:51,683 example DEBUG __main__.MyIntellect :: second_sum is 3\n\t2011-10-04 23:56:51,685 example DEBUG __main__.MyIntellect :: first_sum is 6\n\t2011-10-04 23:56:51,685 example DEBUG __main__.MyIntellect :: second_sum is 6\n\t2011-10-04 23:56:51,687 example DEBUG __main__.MyIntellect :: first_sum is 10\n\t2011-10-04 23:56:51,687 example DEBUG __main__.MyIntellect :: second_sum is 10\n\nSee section 7.3.3.1.2_ ``attributeAction`` for another example.\n\n7.3 Rule Statements (``ruleStmt``)\n----------------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/ruleStmt.jpg\n \n The syntax diagram for a ``ruleStmt``.\n\nA rule statement at its simplest looks like so::\n\n\trule \"print\":\t\n\t\tthen:\n\t\t\tprint(\"hello world!!!!\")\n\nThe rule ``\"print\"`` will always activate and output ``hello world!!!!`` to the \n``sys.stdout``.\n\nA rule will always have an identifier (``id``) in either a ``NAME`` or ``STRING``\ntoken form following Python's naming and ``String`` conventions.\n\nGenerally, a rule will have both a ``when`` portion containing the condition \nof the rule, as of now a ``ruleCondition``, and an ``action`` described by the \n``then`` portion. The ``action`` can be thought of in Python-terms as having more \nspecifically a suite of one ore more actions.\n\nDepending on the evaluation of ``condition``, facts in knowledge will be matched \nand then operated over in the action of the rule. \n\nSuch as in the rule ``\"delete those that don't match\"``, all facts in knowledge \nof type ``ClassD`` who's ``property1`` value is either a ``1`` or ``2`` or ``3``\nwill be deleted in action of the rule.\n\n::\n\n\tfrom intellect.testing.ClassCandD import ClassD\n\t\t\n\trule \"delete those that don't match\":\n\t\twhen:\n\t\t\tnot $bar := ClassD(property1 in [1,2,3])\n\t\tthen:\n\t\t\tdelete $bar\n\n7.3.1 ``agenda-group`` rule property\n------------------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/agendaGroup.jpg\n \n The syntax diagram for a ``agendaGroup``.\n\nOptionally, a rule may have an ``agenda-group`` property that allows it to be \ngrouped in to agenda groups, and fired on an agenda.\n\nSee sections 7.2_ ``attribute`` and 7.3.3.1.2_ ``attributeAction`` for examples \nof the use of this property.\n\n7.3.2 When\n----------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/when.jpg\n \n The syntax diagram for a ``when``.\n\nIf present in rule, it defines the condition on which the rule will be activated.\n\n7.3.2.1 Rule Condition (``condition``)\n--------------------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/condition.jpg\n \n The syntax diagram for a ``condition``.\n \nA rule may have an optional condition, a boolean evaluation, on the state of objects \nin knowledge defined by a Class Constraint (``classConstraint``), and may be \noptionally prepended with ``exists`` as follows::\n\n\trule rule_c:\n\t\twhen:\n\t\t\texists $classB := ClassB(property1.startswith(\"apple\") and property2>5 and test.greaterThanTen(property2) and aMethod() == \"a\")\n\t\tthen:\n\t\t\tprint( \"matches\" + \" exist\" )\n\t\t\ta = 1\n\t\t\tb = 2\n\t\t\tc = a + b\n\t\t\tprint(c)\n\t\t\ttest.helloworld()\n\t\t\t# call MyIntellect's bar method as it is decorated as callable\n\t\t\tbar()\n\nand thus the action will be called once if there are any object in memory matching \nthe condition. The action statements ``modify`` and ``delete`` may not be used in \nthe action if ``exists`` prepends the ``classContraint``.\n\nCurrently, the DSL only supports a single ``classConstraint``, but work is ongoing\nto support more than one.\n\n7.3.2.1.1 A Class Constraint (``classConstraint``)\n--------------------------------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/classConstraint.jpg\n \n The syntax diagram for a ``classConsraint``.\n\nA ``classContraint`` defines how an objects in knowledge will be matched. It defines an \n``OBJECTBINDING``, the Python name of the object's class and the optional ``constraint`` \nby which objects will be matched in knowledge.\n\nThe ``OBJECTBINDING`` is a ``NAME`` token following Python's naming convention prepended\nwith a dollar-sign (``$``).\n\nAs in the case of the Rule Condition example::\n\n\t\t\texists $classB := ClassB(property1.startswith(\"apple\") and property2>5 and test.greaterThanTen(property2) and aMethod() == \"a\")\n\n\n``$classB`` is the ``OBJECTBINDING`` that binds the matches of facts of type\n``ClassB`` in knowledge matching the ``constraint``.\n\nAn ``OBJECTBINDING`` can be further used in the action of the rule, but not in the \ncase where the ``condition`` is prepended with ``exists`` as in the example.\n\n7.3.2.1.2 A Constraint\n----------------------\n\nA ``constraint`` follows the same basic ``and``, ``or``, and ``not`` grammar that Python\nfollows.\n\nAs in the case of the Rule Condition example::\n\n\t\t\texists $classB := ClassB(property1.startswith(\"apple\") and property2>5 and test.greaterThanTen(property2) and aMethod() == \"a\")\n\nAll ``ClassB`` type facts are matched in knowledge that have ``property1`` attributes\nthat ``startwith`` ``apple``, and ``property2`` attributes greater than ``5`` before \nevaluated in hand with ``exist`` statement. More on the rest of the constraint follows\nin the sections below.\n\n7.3.2.1.2.1 Using Regular Expressions\n-------------------------------------\n\nYou can also use regular expressions in constraint by simply importing the\nregular expression library straight from Python and then using like so as\nin the case of the Rule Condition example::\n\n\t\t\t$classB := ClassB( re.search(r\"\\bapple\\b\", property1)!=None and property2>5 and test.greaterThanTen(property2) and aMethod() == \"a\")\n\nThe regular expression ``r\"\\bapple\\b\"`` search is performed on ``property1`` of\nobjects of type ``ClassB`` in knowledge.\n\n7.3.2.1.2.2 Using Methods\n-------------------------\n\nTo rewrite a complicated ``constraint``:\n````````````````````````````````````````\n\nIf you are writing a very complicated ``constraint`` consider moving the \nevaluation necessary for the ``constraint`` into a method of fact being \nreasoned over to increase readability.\n\nAs in the case of the Rule Condition example, it could be rewritten to::\n\n\t\t\t$classB := ClassB(property1ContainsTheStrApple() and property2>5 and test.greaterThanTen(property2) and aMethod() == \"a\")\n\nIf you were to add the method to ClassB::\n\n\tdef property1ContainsTheStrApple()\n\t\treturn re.search(r\"\\bapple\\b\", property1) != None\n\nOf a class and/or instance:\n```````````````````````````\n\nThis example, also demonstrates how the ``test`` module function ``greaterThanTen`` \ncan be messaged the instance's ``property2`` attribute and the function's return \nevaluated, and a call to the instance's ``aMethod`` method can be evaluated for \na return of ``\"a\"``.\n\n7.3.3 Then\n----------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/then.jpg\n \n The syntax diagram for a ``then``.\n\nIs used to define the suite of one-or-more ``action`` statements to be called\nfiring the rule, when the rule is said to be activated.\n\n7.3.3.1 Rule Action (Suite of Actions)\n--------------------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/action.jpg\n \n The syntax diagram for an ``action``.\n\nRules may have a suite of one or more actions used in process of doing something, \ntypically to achieve an aim.\n\n7.3.3.1.1 Simple Statements (``simpleStmt``)\n--------------------------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/simpleStmt.jpg\n \n The syntax diagram for a ``simpleStmt``.\n\n``simpleStmts`` are supported actions of a rule, and so one can do the following::\n\n\trule rule_c:\n\t\twhen:\n\t\t\texists $classB := ClassB(property1.startswith(\"apple\") and property2>5 and test.greaterThanTen(property2) and aMethod() == \"a\")\n\t\tthen:\n\t\t\tprint(\"matches\" + \" exist\")\n\t\t\ta = 1\n\t\t\tb = 2\n\t\t\tc = a + b\n\t\t\tprint(c)\n\t\t\ttest.helloworld()\n\t\t\tbar()\n\nThe ``simpleStmt`` in the action will be executed if any facts in knowledge \nexist matching the condition.\n\nTo keep the policy files from turning into just another Python script you\nwill want to keep as little code out of the suite of actions and thus the policy \nfile was possible... You will want to focus on using ``modify``, ``delete``, \n``insert``, ``halt`` before heavily using large amounts of simple statements. This\nis why ``action`` supports a limited Python grammar. ``if``, ``for``, ``while`` etc\nare not supported, only Python's ``expressionStmt`` statements are supported.\n\n.. _7.3.3.1.2:\n\n7.3.3.1.2 ``attributeAction``\n-----------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/attributeStmt.jpg\n \n The syntax diagram for a ``attributeStmt``.\n \n``attributeAction`` actions are used to create, delete, or modify a policy \nattribute.\n\nFor example::\n\n\ti = 0\n\t\n\trule rule_e:\n\t\tagenda-group \"1\"\n\t\tthen:\n\t\t\tattribute i = i + 1\n\t\t\tprint i\n\t\n\trule rule_f:\n\t\tagenda-group \"2\"\n\t\tthen:\n\t\t\tattribute i = i + 1\n\t\t\tprint i\n\t\n\trule rule_g:\n\t\tagenda-group \"3\"\n\t\tthen:\n\t\t\tattribute i = i + 1\n\t\t\tprint i\n\t\n\trule rule_h:\n\t\tagenda-group \"4\"\n\t\tthen:\n\t\t\t# the 'i' variable is scoped to then portion of the rule\n\t\t\ti = 0\n\t\t\tprint i\n\t\n\trule rule_i:\n\t\tagenda-group \"5\"\n\t\tthen:\n\t\t\tattribute i += 1\n\t\t\tprint i\n\t\t\t# the 'i' variable is scoped to then portion of the rule\n\t\t\ti = 0\n\t\n\trule rule_j:\n\t\tagenda-group \"6\"\n\t\tthen:\n\t\t\tattribute i += 1\n\t\t\tprint i\n\nIf the rules engine is instructed to reason seeking to activate \nrules on agenda in the order describe by the Python list\n``[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"]`` like so::\n\n\tclass MyIntellect(Intellect):\n\t\tpass\n\t\n\tif __name__ == \"__main__\":\n\t\n\t\tmyIntellect = MyIntellect()\n\t\n\t\tpolicy_c = myIntellect.learn(Intellect.local_file_uri\"./rulesets/test_c.policy\"))\n\t\n\t\tmyIntellect.reason([\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"])\n\nThe following output will result::\n\n\t1\n\t2\n\t3\n\t0\n\t4\n\t5\n\nWhen firing ``rule_e`` the policy attribute ``i`` will be incremented by a value \nof ``1``, and print ``1``, same with ``rule_f`` and ``rule_g``, but ``rule_h`` \nprints 0. The reason for this is the ``i`` variable is scoped to ``then`` portion \nof the rule. ``Rule_i`` further illustrates scoping: the policy attribute ``i``\nis further incremented by ``1`` and is printed, and then a variable ``i`` scoped to\n``then`` portion of the rule initialized to ``0``, but this has no impact on\nthe policy attribute ``i`` for when ``rule_j`` action is executed firing the rule\nthe value of ``6`` is printed.\n\n7.3.3.1.3 ``learn`` action\n--------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/learnAction.jpg\n :scale: 50 %\n \n The syntax diagram for a ``learnAction``.\n\nA rule entitled ``\"Time to buy new sheep?\"`` might look like the following::\n\n\trule \"Time to buy new sheep?\":\n\t\twhen:\n\t\t\t$buyOrder := BuyOrder( )\n\t\tthen:\n\t\t\tprint( \"Buying a new sheep.\" )\n\t\t\tmodify $buyOrder:\n\t\t\t\tcount = $buyOrder.count - 1\n\t\t\tlearn BlackSheep()\n\nThe rule above illustrates the use of a ``learn`` action to learn/insert \na ``BlackSheep`` fact. The same rule can also be written as the following\nusing ``insert``::\n\n\trule \"Time to buy new sheep?\":\n\t\twhen:\n\t\t\t$buyOrder := BuyOrder( )\n\t\tthen:\n\t\t\tprint( \"Buying a new sheep.\" )\n\t\t\tmodify $buyOrder:\n\t\t\t\tcount = $buyOrder.count - 1\n\t\t\tinsert BlackSheep()\n\n7.3.3.1.4 ``forget`` action\n---------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/forgetAction.jpg\n \n The syntax diagram for a ``forgetAction``.\n\n\nA rule entitled ``\"Remove empty buy orders\"`` might look like the following::\n\n\trule \"Remove empty buy orders\":\n\t\twhen:\n\t\t\t$buyOrder := BuyOrder( count == 0 )\n\t\tthen:\n\t\t\tforget $buyOrder\n\n\nThe rule above illustrates the use of a ``forget`` action to forget/delete \neach match returned by the rule's condition. The same rule can also be written \nas the following using ``delete``::\n\n\trule \"Remove empty buy orders\":\n\t\twhen:\n\t\t\t$buyOrder := BuyOrder( count == 0 )\n\t\tthen:\n\t\t\tdelete $buyOrder\n\nNote: cannot be used in conjunction with ``exists``.\n\n7.3.3.1.5 ``modify`` action\n---------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/modifyAction.jpg\n \n The syntax diagram for a ``modifyAction``.\n\nThe following rule::\n\n\trule \"Time to buy new sheep?\":\n\t\twhen:\n\t\t\t$buyOrder := BuyOrder( )\n\t\tthen:\n\t\t\tprint( \"Buying a new sheep.\" )\n\t\t\tmodify $buyOrder:\n\t\t\t\tcount = $buyOrder.count - 1\n\t\t\tlearn BlackSheep()\n\n\nillustrates the use of a ``modify`` action to modify each ``BuyOrder`` match \nreturned by the rule's condition. Cannot be used in conjunction with ``exists``\nrule conditions. The ``modify`` action can also be used to chain rules, what \nyou do is modify the fact (toggle a boolean property, set a property's value,\netc) and then use this property to evaluate in the proceeding rule.\n\n\n7.3.3.1.6 ``halt`` action\n-------------------------\n\n.. figure:: https://github.com/nemonik/Intellect/raw/master/images/haltAction.jpg\n \n The syntax diagram for a ``haltAction``.\n\nThe following rule::\n\n\trule \"End policy\":\n\t\tthen:\n\t\t\tlog(\"Finished reasoning over policy.\", \"example\", logging.DEBUG)\n\t\t\thalt\n\nillustrates the use of a ``halt`` action to tell the rules engine to halt \nreasoning over the policy.\n\n8. Creating and using a Rules Engine with a single policy\n---------------------------------------------------------\n\nAt its simplest a rules engine can be created and used like so::\n\n\timport sys, logging\n\t\n\tfrom intellect.Intellect import Intellect\n\tfrom intellect.Intellect import Callable\n\t\n\t# set up logging\n\tlogging.basicConfig(level=logging.DEBUG,\n\tformat='%(asctime)s %(name)-12s%(levelname)-8s%(message)s', stream=sys.stdout)\n\t\n\tintellect = Intellect()\n\t\n\tpolicy_a = intellect.learn(Intellect.local_file_uri(\"../rulesets/test_a.policy\"))\n\t\n\tintellect.reason()\n\t\n\tintellect.forget_all()\n\n\nIt may be preferable for you to sub-class ``intellect.Intellect.Intellect`` class in \norder to add ``@Callable`` decorated methods that will in turn permit these methods\nto be called from the action of the rule.\n \nFor example, ``MyIntellect`` is created to sub-class ``Intellect``::\n\n\timport sys, logging\n\t\n\tfrom intellect.Intellect import Intellect\n\tfrom intellect.Intellect import Callable\n\n\tclass MyIntellect(Intellect):\n\t\n\t\t@Callable\n\t\tdef bar(self):\n\t\t\tself.log(logging.DEBUG, \">>>>>>>>>>>>>> called MyIntellect's bar method as it was decorated as callable.\")\n\t\n\tif __name__ == \"__main__\":\n\t\n\t\t# set up logging\n\t\tlogging.basicConfig(level=logging.DEBUG,\n\t\t\tformat='%(asctime)s %(name)-12s%(levelname)-8s%(message)s',\n\t\t\t#filename=\"rules.log\")\n\t\t\tstream=sys.stdout)\n\t\n\t\tprint \"*\"*80\n\t\tprint \"\"\"create an instance of MyIntellect extending Intellect, create some facts, and exercise the MyIntellect's ability to learn and forget\"\"\"\n\t\tprint \"*\"*80\n\t\n\t\tmyIntellect = MyIntellect()\n\t\n\t\tpolicy_a = myIntellect.learn(Intellect.local_file_uri(\"../rulesets/test_a.policy\"))\n\t\n\t\tmyIntellect.reason()\n\t\n\t\tmyIntellect.forget_all()\n\n\nThe policy could then be authored, where the ``MyIntellect`` class's ``bar`` method \nis called for matches to the rule condition, like so::\n\n\tfrom intellect.testing.subModule.ClassB import ClassB\n\timport intellect.testing.Test as Test\n\timport logging\n\t\n\tfruits_of_interest = [\"apple\", \"grape\", \"mellon\", \"pear\"]\n\tcount = 5\n\t\n\trule rule_a:\n\t\tagenda-group test_a\n\t\twhen:\n\t\t\t$classB := ClassB( property1 in fruits_of_interest and property2>count ) \n\t\tthen:\n\t\t\t# mark the 'ClassB' matches in memory as modified\n\t\t\tmodify $classB:\n\t\t\t\tproperty1 = $classB.property1 + \" pie\"\n\t\t\t\tmodified = True\n\t\t\t\t# increment the match's 'property2' value by 1000\n\t\t\t\tproperty2 = $classB.property2 + 1000\n\t\n\t\t\tattribute count = $classB.property2\n\t\t\tprint \"count = {0}\".format( count )\n\t\n\t\t\t# call MyIntellect's bar method as it is decorated as callable\n\t\t\tbar()\n\t\t\tlog(logging.DEBUG, \"rule_a fired\")", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://github.com/nemonik/Intellect", "keywords": "intellect rules engine dsl policy", "license": "BSD, 4-clause license", "maintainer": null, "maintainer_email": null, "name": "Intellect", "package_url": "https://pypi.org/project/Intellect/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/Intellect/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://github.com/nemonik/Intellect" }, "release_url": "https://pypi.org/project/Intellect/1.4.9/", "requires_dist": null, "requires_python": null, "summary": "A Domain-specific language and Rules Engine for Python", "version": "1.4.9" }, "last_serial": 784548, "releases": { "1.4.2": [ { "comment_text": "", "digests": { "md5": "4ddb64337ef3ff065976b784c6e2ed55", "sha256": "32129f56ea0e2f72b4fe02ab53cba011b6453cfacfbb19cf51e9df04b54d1abe" }, "downloads": -1, "filename": "Intellect-1.4.2.tar.gz", "has_sig": false, "md5_digest": "4ddb64337ef3ff065976b784c6e2ed55", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 80863, "upload_time": "2011-10-25T01:02:33", "url": "https://files.pythonhosted.org/packages/d5/fa/984c39635232805380ac8b603a5f51ba4d8b80d95143b1d106eed00261d7/Intellect-1.4.2.tar.gz" } ], "1.4.3": [ { "comment_text": "", "digests": { "md5": "a85b5d181791edf0a2605e6220796d31", "sha256": "4ec5ad4efffd721118260e4ac5308e664b73db5083447d32d8ae0e6a212bc7ae" }, "downloads": -1, "filename": "Intellect-1.4.3.tar.gz", "has_sig": false, "md5_digest": "a85b5d181791edf0a2605e6220796d31", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 81452, "upload_time": "2011-11-08T17:32:10", "url": "https://files.pythonhosted.org/packages/71/d3/cc9d17a461f047649d022b78d18d05623190f011b65afd1ac8434fc8f915/Intellect-1.4.3.tar.gz" } ], "1.4.4": [ { "comment_text": "", "digests": { "md5": "7c0e187e4789cd51fecdff3d9f6e6539", "sha256": "93032ad3eb62890ff163997c47f5a92264919631ff83d8ea63c0d607415b5cad" }, "downloads": -1, "filename": "Intellect-1.4.4.tar.gz", "has_sig": false, "md5_digest": "7c0e187e4789cd51fecdff3d9f6e6539", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 82022, "upload_time": "2011-12-30T03:40:21", "url": "https://files.pythonhosted.org/packages/bb/5a/be2b3ef6ea72a37e30cb1681742a5c8a8dc12a4ad4471e610ae63e4c7040/Intellect-1.4.4.tar.gz" } ], "1.4.5": [ { "comment_text": "", "digests": { "md5": "d62ae0737ef366f54d89763fcee5c00e", "sha256": "0fd035dd9f930cad5d6c353b135b3a114169ed23615eda0ae40046009fbfe8ed" }, "downloads": -1, "filename": "Intellect-1.4.5.tar.gz", "has_sig": false, "md5_digest": "d62ae0737ef366f54d89763fcee5c00e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 82005, "upload_time": "2011-12-30T05:53:13", "url": "https://files.pythonhosted.org/packages/17/15/c2bb149f67a55adb22e424cf6000127356377ff8164cb75c5136a74929d1/Intellect-1.4.5.tar.gz" } ], "1.4.6": [ { "comment_text": "", "digests": { "md5": "7f776848fb9e98ca94de0e6c0baf7aa9", "sha256": "1d7c3a1de6b1b2ddb3f75149bb9a53af157ca80240dd41c9ba00ba15f5cc5fc2" }, "downloads": -1, "filename": "Intellect-1.4.6.tar.gz", "has_sig": false, "md5_digest": "7f776848fb9e98ca94de0e6c0baf7aa9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 47607, "upload_time": "2012-04-09T19:57:31", "url": "https://files.pythonhosted.org/packages/8e/4f/6e2a6e7cee072474ccd0559fe9713ec749c152062f2e01ba45e320af220f/Intellect-1.4.6.tar.gz" } ], "1.4.7": [ { "comment_text": "", "digests": { "md5": "3c7ed4d953f01d97bf367dfa1ef6c240", "sha256": "99f59c8a5c56cad68aa49ba6f0a5b0a275fa8580915427db5e2151e6a619a127" }, "downloads": -1, "filename": "Intellect-1.4.7.tar.gz", "has_sig": false, "md5_digest": "3c7ed4d953f01d97bf367dfa1ef6c240", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 81994, "upload_time": "2012-04-12T19:35:33", "url": "https://files.pythonhosted.org/packages/2b/89/092237d301b788ab6cbb462cc7253104ac614da846141b6ce977585d66dc/Intellect-1.4.7.tar.gz" } ], "1.4.8": [ { "comment_text": "", "digests": { "md5": "fff9590fbb25f6c399284b503b8c4197", "sha256": "d4807f3cb55a6094f62f0250ffe97160eaa340af06546c87f0ddac085bf3b729" }, "downloads": -1, "filename": "Intellect-1.4.8.tar.gz", "has_sig": false, "md5_digest": "fff9590fbb25f6c399284b503b8c4197", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 82390, "upload_time": "2012-04-16T23:10:48", "url": "https://files.pythonhosted.org/packages/f9/c6/898f6e7aeae89197058c25a56e09fb7e9c49d745fe9525c1a13b6ed308a1/Intellect-1.4.8.tar.gz" } ], "1.4.8.1": [ { "comment_text": "", "digests": { "md5": "2765f8799288fc0b60e6aa075f231d4d", "sha256": "3cc3a27b0ddae537b84595b3b22b388f4d803fd16b632fb0e4eb3e1787614ef0" }, "downloads": -1, "filename": "Intellect-1.4.8.1.tar.gz", "has_sig": false, "md5_digest": "2765f8799288fc0b60e6aa075f231d4d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 82398, "upload_time": "2012-04-17T02:11:14", "url": "https://files.pythonhosted.org/packages/a6/d4/2367c4cc31b0a6051de2ad304f9e28ec859fc7470982a8bb388b50b1d6b3/Intellect-1.4.8.1.tar.gz" } ], "1.4.8.2": [ { "comment_text": "", "digests": { "md5": "72b019a3bddfbc345fa0f0ea423ab85b", "sha256": "d4d053d589bbf434e959bc507cf7d0edf33594e36228ca2d7d87594857d06979" }, "downloads": -1, "filename": "Intellect-1.4.8.2.tar.gz", "has_sig": false, "md5_digest": "72b019a3bddfbc345fa0f0ea423ab85b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 82423, "upload_time": "2012-04-25T03:49:05", "url": "https://files.pythonhosted.org/packages/e1/86/272d744f680b6a7c73112f2af8f79c7d5c5d935838446828aedd53361db3/Intellect-1.4.8.2.tar.gz" } ], "1.4.8.3": [ { "comment_text": "", "digests": { "md5": "a4189aa9de44d020ef9f2927a4e91507", "sha256": "c2ca824cc3f5220c599fe6fc07da57956dec6d99aa9001f9ec9dd5796c79dbf1" }, "downloads": -1, "filename": "Intellect-1.4.8.3.tar.gz", "has_sig": false, "md5_digest": "a4189aa9de44d020ef9f2927a4e91507", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 82885, "upload_time": "2012-05-02T23:41:36", "url": "https://files.pythonhosted.org/packages/0f/4f/429743384b397a4e188f89d4a079b3b365e6cbc9fd7216350a5e37428966/Intellect-1.4.8.3.tar.gz" } ], "1.4.8.4": [ { "comment_text": "", "digests": { "md5": "be5df8bf1731983cb32ab332a6b7e825", "sha256": "512843d5d517bf5efe258f5dca114eb0ab5c3ddd5fc03050623d848314b3bad4" }, "downloads": -1, "filename": "Intellect-1.4.8.4.tar.gz", "has_sig": false, "md5_digest": "be5df8bf1731983cb32ab332a6b7e825", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 82397, "upload_time": "2012-05-29T23:06:46", "url": "https://files.pythonhosted.org/packages/43/c2/31e3c2114499b0eb714ec861c741a4ba28c2fd534aa26f15ae81c05a7662/Intellect-1.4.8.4.tar.gz" } ], "1.4.8.5": [ { "comment_text": "", "digests": { "md5": "52ca489424d575b0282c910494d1abf0", "sha256": "6db25fdc0ddedaeaf142d84b449997d44ac516b70b149935c1c2fec94377821a" }, "downloads": -1, "filename": "Intellect-1.4.8.5.tar.gz", "has_sig": false, "md5_digest": "52ca489424d575b0282c910494d1abf0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 82408, "upload_time": "2012-06-11T17:25:05", "url": "https://files.pythonhosted.org/packages/aa/c7/826c5129a9d489a6a51ba9d5f9ccc66ca205bb26dd6ed695159f81af5b15/Intellect-1.4.8.5.tar.gz" } ], "1.4.8.6": [ { "comment_text": "", "digests": { "md5": "8f69e21be511f5fcbbd49e1c833689ce", "sha256": "f60b9060d9a8183a2feb703ac60c0c612db0aa9738530fe7f1b7bde765f954b7" }, "downloads": -1, "filename": "Intellect-1.4.8.6.tar.gz", "has_sig": false, "md5_digest": "8f69e21be511f5fcbbd49e1c833689ce", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 83679, "upload_time": "2012-08-24T21:37:36", "url": "https://files.pythonhosted.org/packages/ce/92/ae9569bed3baa8af490ab54d45d685ec460148b3bad7bcda1727bedd2ea8/Intellect-1.4.8.6.tar.gz" } ], "1.4.8.7": [ { "comment_text": "", "digests": { "md5": "c9c4e81b491eef51c88e6bdc130eef9d", "sha256": "1fd03807229daa4a5dc1cf1d5cd26685ea6fca4caa6999d4c9657cd23beab4c0" }, "downloads": -1, "filename": "Intellect-1.4.8.7.tar.gz", "has_sig": false, "md5_digest": "c9c4e81b491eef51c88e6bdc130eef9d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 83684, "upload_time": "2012-08-24T21:53:47", "url": "https://files.pythonhosted.org/packages/41/3a/165f461c69defef4d353ad9a47bb9915adbe42ab992b2c182215ab30cc71/Intellect-1.4.8.7.tar.gz" } ], "1.4.8.8": [ { "comment_text": "", "digests": { "md5": "ce7d8caae010e56fd588348f367cb1c1", "sha256": "9e150d347ec3a2e7e867b1c96036ab19b6471683346c6a4c388f0fa9e485999a" }, "downloads": -1, "filename": "Intellect-1.4.8.8.tar.gz", "has_sig": false, "md5_digest": "ce7d8caae010e56fd588348f367cb1c1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 83700, "upload_time": "2012-08-24T21:58:50", "url": "https://files.pythonhosted.org/packages/3e/ba/f3ef4ec19cd0015324274ca55f729babdca2c140e9327d367c173c4b2688/Intellect-1.4.8.8.tar.gz" } ], "1.4.9": [ { "comment_text": "", "digests": { "md5": "c1808b2f8454c4dcdc52f2c17764ae02", "sha256": "38698a8dc42f3cc0551c9ebeeb967be685744b3b8601c663da35801813045189" }, "downloads": -1, "filename": "Intellect-1.4.9.tar.gz", "has_sig": false, "md5_digest": "c1808b2f8454c4dcdc52f2c17764ae02", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 83924, "upload_time": "2012-08-31T18:26:34", "url": "https://files.pythonhosted.org/packages/e6/83/07dd60df9d7099f30b3449494477fc00286fbeb9e9203a47fa1c21ef37d3/Intellect-1.4.9.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c1808b2f8454c4dcdc52f2c17764ae02", "sha256": "38698a8dc42f3cc0551c9ebeeb967be685744b3b8601c663da35801813045189" }, "downloads": -1, "filename": "Intellect-1.4.9.tar.gz", "has_sig": false, "md5_digest": "c1808b2f8454c4dcdc52f2c17764ae02", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 83924, "upload_time": "2012-08-31T18:26:34", "url": "https://files.pythonhosted.org/packages/e6/83/07dd60df9d7099f30b3449494477fc00286fbeb9e9203a47fa1c21ef37d3/Intellect-1.4.9.tar.gz" } ] }