Skip to content

Commit

Permalink
Merge branch 'release/1.9.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
nilp0inter committed Nov 16, 2019
2 parents c5e3f51 + a14cadf commit 9cce965
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 6 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ python:
- "3.5"
- "3.6"
- "3.7"
- "3.8"
install:
- pip install pipenv
- pipenv install --dev --ignore-pipfile --skip-lock
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
CHANGELOG
---------

1.9.3
~~~~~

* Resolve #11 that was introduced with #7, that caused the same behavior
under different conditions.


1.9.2
~~~~~

Expand Down
4 changes: 3 additions & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ pycodestyle = "*"
pydocstyle = "*"
codecov = "*"
twine = "*"
ipython = "*"

[packages]
tox = "*"
experta = {editable = true,path = "."}

[requires]
python_version = "3.4"
python_version = "3.7"
166 changes: 166 additions & 0 deletions docs/examples/EXISTS.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# EXISTS Conditional Element\n",
"\n",
"With exists you can test if a group of patterns is satisfied by at least one set of facts.\n",
"\n",
"The rule will be fired once.\n",
"\n",
"Please note that you although you can use MATCH to pattern match between matches inside the EXISTS group, **those matches can't be used as parameters in the RHS of the rule** (because more than one group of patterns can match and we are only firing once). Trying to use the matched field as parameter will result in a **TypeError**.\n",
"\n",
"This is a direct translation to Experta of [this](https://www.csie.ntu.edu.tw/~sylee/courses/clips/bpg/node5.4.6.html) Clips example about the EXISTS Conditional Element"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from experta import *"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class Goal(Fact):\n",
" pass\n",
"\n",
"class Hero(Fact):\n",
" name = Field(str)\n",
" status = Field(str, default=\"unoccupied\")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"class KE(KnowledgeEngine):\n",
" @DefFacts()\n",
" def goal_and_heroes(self):\n",
" yield Goal('save-the-day')\n",
" yield Hero(name=\"Death Defying Man\")\n",
" yield Hero(name=\"Stupendous Man\")\n",
" yield Hero(name=\"Incredible Man\")\n",
" @Rule(\n",
" Goal('save-the-day'),\n",
" EXISTS(\n",
" Hero(status=\"unoccupied\")\n",
" )\n",
" )\n",
" def save_the_day(self):\n",
" print(\"The day is saved\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"ke = KE()\n",
"ke.reset()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0: save_the_day {InitialFact(), Goal('save-the-day')}"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ke.agenda"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"FactList([(0, InitialFact()),\n",
" (1, Goal('save-the-day')),\n",
" (2, Hero(name='Death Defying Man')),\n",
" (3, Hero(name='Stupendous Man')),\n",
" (4, Hero(name='Incredible Man'))])"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ke.facts"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"# TODO: Implement (matches ) function"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The day is saved\n"
]
}
],
"source": [
"ke.run()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.8"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
2 changes: 1 addition & 1 deletion experta/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '1.9.2'
__version__ = '1.9.3'

try:
from .conditionalelement import AND, OR, NOT, TEST, EXISTS, FORALL
Expand Down
4 changes: 2 additions & 2 deletions experta/agenda.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ def __init__(self):
def __repr__(self): # pragma: no cover
return "\n".join(
"{idx}: {rule} {facts}".format(idx=idx,
rule=getattr(act.activation.rule,
rule=getattr(act.rule,
'__name__',
'[anonymous]'),
facts=act.activation.facts)
facts=act.facts)
for idx, act in enumerate(self.activations))

def get_next(self):
Expand Down
3 changes: 2 additions & 1 deletion experta/strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ def _update_agenda(self, agenda, added, removed):
act.key = self.get_key(act)
try:
idx = bisect.bisect_left(agenda.activations, act)
del agenda.activations[idx]
if agenda.activations[idx] == act:
del agenda.activations[idx]
except IndexError:
pass

Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ classifiers =
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: Implementation :: PyPy
authors = Roberto Abdelkader Martínez Pérez
authors_email = [email protected]
Expand Down
33 changes: 33 additions & 0 deletions tests/unit/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -986,3 +986,36 @@ def first(self, f):
ke.declare(Fact(s=False))
ke.run()
assert executed == [first, second]

def test_last_activation_is_executed_2():
from experta import KnowledgeEngine, Rule, Fact, AS, DefFacts
from experta import NOT, W, MATCH

executed = None

class KE(KnowledgeEngine):
@DefFacts()
def _initial_action(self):
yield Fact(action="greet")

@Rule(Fact(action='greet'),
NOT(Fact(name=W())))
def ask_name(self):
self.declare(Fact(name="foo"))

@Rule(Fact(action='greet'),
NOT(Fact(location=W())))
def ask_location(self):
self.declare(Fact(location="bar"))

@Rule(Fact(action='greet'),
Fact(name=MATCH.name),
Fact(location=MATCH.location))
def greet(self, name, location):
nonlocal executed
executed = (name, location)

ke = KE()
ke.reset()
ke.run()
assert executed == ("foo", "bar")
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py35, py36, py37, pypy3
envlist = py35, py36, py37, py38, pypy3

[testenv]
deps = pipenv
Expand Down

0 comments on commit 9cce965

Please sign in to comment.