From ecbae9e42c6318c11879cc0ad2685a15899f9c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C3=BCce=20Tekol?= Date: Mon, 15 Jul 2024 18:38:42 +0300 Subject: [PATCH] Project update (#160) - Revert to 8b002d44b08cc485ce95dd1326d99cbdb685e473 - Updated development dependencies - Added a Github action to run tests and coverage - Formatted the code with black --- .github/workflows/tests.yaml | 45 +++ Makefile | 5 +- dev-requirements.txt | 2 + examples/coins/coins.py | 15 +- examples/coins/coins_new.py | 10 +- examples/create_term.py | 13 +- examples/draughts/puzzle1.py | 15 +- examples/father.py | 17 +- examples/hanoi/hanoi.py | 22 +- examples/hanoi/hanoi_simple.py | 12 +- examples/knowledgebase.py | 9 +- examples/register_foreign.py | 11 +- examples/register_foreign_simple.py | 14 +- examples/sendmoremoney/money.py | 6 +- examples/sendmoremoney/money_new.py | 10 +- examples/sudoku/sudoku.py | 56 +-- examples/sudoku/sudoku_daily.py | 27 +- pyswip/core.py | 508 +++++++++++++++------------- pyswip/easy.py | 97 +++--- pyswip/prolog.py | 58 ++-- setup.py | 53 +-- test-requirements.txt | 1 - tests/test_examples.py | 166 ++++----- tests/test_foreign.py | 55 ++- tests/test_issues.py | 115 ++++--- tests/test_prolog.py | 20 +- 26 files changed, 771 insertions(+), 591 deletions(-) create mode 100644 .github/workflows/tests.yaml create mode 100644 dev-requirements.txt delete mode 100644 test-requirements.txt diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..b9fbe17 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,45 @@ +name: "Run tests" + +on: + push: + branches: + - "master" + + pull_request_target: + branches: + - "master" + +jobs: + + run-tests: + + name: "Run tests with Python ${{ matrix.python-version }} on ${{ matrix.os }}" + + strategy: + matrix: + python-version: [ '3.8', '3.12' ] + os: [ "ubuntu-24.04" ] + fail-fast: false + + runs-on: "${{ matrix.os }}" + + steps: + + - uses: "actions/checkout@v2" + + - name: "Set up Python ${{ matrix.python-version }}" + uses: "actions/setup-python@v2" + with: + python-version: "${{ matrix.python-version }}" + + - name: "Install test dependencies" + run: | + pip install -r dev-requirements.txt + + - name: "Check style" + run: | + make check + + - name: "Run tests" + run: | + make cover diff --git a/Makefile b/Makefile index 9c9fe99..c43d2f8 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,13 @@ clean: rm -rf dist build pyswip.egg-info cover: - py.test --cov=pyswip tests + py.test tests --verbose --cov=pyswip test: py.test tests --verbose upload: twine upload dist/* + +check: + black --check . \ No newline at end of file diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..22f5e54 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,2 @@ +black==24.4.2 +pytest-cov==5.0.0 diff --git a/examples/coins/coins.py b/examples/coins/coins.py index 8a14763..670fac7 100644 --- a/examples/coins/coins.py +++ b/examples/coins/coins.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -31,19 +31,20 @@ except NameError: pass + def main(): prolog = Prolog() prolog.consult("coins.pl") count = int(input("How many coins (default: 100)? ") or 100) total = int(input("What should be the total (default: 500)? ") or 500) - for i, soln in enumerate(prolog.query("coins(S, %d, %d)." % (count,total))): + for i, soln in enumerate(prolog.query("coins(S, %d, %d)." % (count, total))): S = zip(soln["S"], [1, 5, 10, 50, 100]) print(i, end=" ") for c, v in S: - print("%dx%d" % (c,v), end=" ") + print("%dx%d" % (c, v), end=" ") print() - list(prolog.query("coins(S, %d, %d)." % (count,total))) + list(prolog.query("coins(S, %d, %d)." % (count, total))) + - if __name__ == "__main__": main() diff --git a/examples/coins/coins_new.py b/examples/coins/coins_new.py index 87b480d..6f3b403 100644 --- a/examples/coins/coins_new.py +++ b/examples/coins/coins_new.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -47,11 +47,11 @@ def main(): s = zip(S.value, [1, 5, 10, 50, 100]) print(i, end=" ") for c, v in s: - print("%dx%d" % (c,v), end=" ") + print("%dx%d" % (c, v), end=" ") print() i += 1 q.closeQuery() - + if __name__ == "__main__": main() diff --git a/examples/create_term.py b/examples/create_term.py index 450458c..8bb33f0 100644 --- a/examples/create_term.py +++ b/examples/create_term.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,9 +24,10 @@ from pyswip.core import * from pyswip.prolog import Prolog + def main(): prolog = Prolog() - + a1 = PL_new_term_refs(2) a2 = a1 + 1 t = PL_new_term_ref() @@ -40,9 +41,9 @@ def main(): PL_cons_functor_v(t, animal2, a1) PL_cons_functor_v(ta, assertz, t) PL_call(ta, None) - + print(list(prolog.query("animal(X,Y)", catcherrors=True))) - + if __name__ == "__main__": main() diff --git a/examples/draughts/puzzle1.py b/examples/draughts/puzzle1.py index cf0abc7..6cc0614 100644 --- a/examples/draughts/puzzle1.py +++ b/examples/draughts/puzzle1.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -54,12 +54,13 @@ def main(): # [NW,N,NE,W,E,SW,S,SE] print("%d %d %d" % tuple(B[:3])) - print("%d %d" % tuple(B[3:5])) - print("%d %d %d" % tuple(B[5:])) + print("%d %d" % tuple(B[3:5])) + print("%d %d %d" % tuple(B[5:])) cont = input("Press 'n' to finish: ") - if cont.lower() == "n": break + if cont.lower() == "n": + break + - if __name__ == "__main__": main() diff --git a/examples/father.py b/examples/father.py index 52bdd61..d402f83 100644 --- a/examples/father.py +++ b/examples/father.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -36,21 +36,22 @@ def main(): p.assertz("father(john,gina)") p.assertz("mother(jane,mich)") - X = Variable(); Y = Variable(); Z = Variable() + X = Variable() + Y = Variable() + Z = Variable() listing = Functor("listing", 1) call(listing(father)) - q = Query(father("john",Y), mother(Z,Y)) + q = Query(father("john", Y), mother(Z, Y)) while q.nextSolution(): print(Y.value, Z.value) - q.closeQuery() # Newer versions of SWI-Prolog do not allow nested queries + q.closeQuery() # Newer versions of SWI-Prolog do not allow nested queries print("\nQuery with strings\n") for s in p.query("father(john,Y),mother(Z,Y)"): print(s["Y"], s["Z"]) + if __name__ == "__main__": main() - - diff --git a/examples/hanoi/hanoi.py b/examples/hanoi/hanoi.py index 3a170a6..9b683b0 100644 --- a/examples/hanoi/hanoi.py +++ b/examples/hanoi/hanoi.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -36,22 +36,22 @@ class Notifier: def __init__(self, fun): self.fun = fun - + def notify(self, t): return not self.fun(t) + notify.arity = 1 - + class Tower: def __init__(self, N=3, interactive=False): - """N is the number of disks - """ + """N is the number of disks""" self.N = N self.disks = dict(left=deque(range(N, 0, -1)), center=deque(), right=deque()) self.started = False self.interactive = interactive self.step = 0 - + def move(self, r): if not self.started: self.step += 1 @@ -62,7 +62,7 @@ def move(self, r): disks[str(r[1])].append(disks[str(r[0])].pop()) self.step += 1 return self.draw() - + def draw(self): disks = self.disks print("\n Step", self.step) @@ -85,7 +85,7 @@ def draw(self): def main(): N = 3 INTERACTIVITY = True - + prolog = Prolog() tower = Tower(N, INTERACTIVITY) notifier = Notifier(tower.move) @@ -93,6 +93,6 @@ def main(): prolog.consult("hanoi.pl") list(prolog.query("hanoi(%d)" % N)) - + if __name__ == "__main__": main() diff --git a/examples/hanoi/hanoi_simple.py b/examples/hanoi/hanoi_simple.py index 91c217d..8720f1d 100644 --- a/examples/hanoi/hanoi_simple.py +++ b/examples/hanoi/hanoi_simple.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,16 +27,18 @@ N = 3 # Number of disks + def main(): def notify(t): print("move disk from %s pole to %s pole." % tuple(t)) + notify.arity = 1 - + prolog = Prolog() registerForeign(notify) prolog.consult("hanoi.pl") list(prolog.query("hanoi(%d)" % N)) - + if __name__ == "__main__": main() diff --git a/examples/knowledgebase.py b/examples/knowledgebase.py index 8937f76..c463985 100644 --- a/examples/knowledgebase.py +++ b/examples/knowledgebase.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -53,5 +53,6 @@ def main(): print(X.value) q.closeQuery() + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/examples/register_foreign.py b/examples/register_foreign.py index 5a5456a..cd7ad20 100644 --- a/examples/register_foreign.py +++ b/examples/register_foreign.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,12 +26,13 @@ def atom_checksum(*a): if isinstance(a[0], Atom): - r = sum(ord(c)&0xFF for c in str(a[0])) - a[1].value = r&0xFF + r = sum(ord(c) & 0xFF for c in str(a[0])) + a[1].value = r & 0xFF return True else: return False + p = Prolog() registerForeign(atom_checksum, arity=2) print(list(p.query("X='Python', atom_checksum(X, Y)", catcherrors=False))) diff --git a/examples/register_foreign_simple.py b/examples/register_foreign_simple.py index 0b10d30..b3619a1 100644 --- a/examples/register_foreign_simple.py +++ b/examples/register_foreign_simple.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,6 +30,8 @@ def hello(t): print("Hello,", t) + + hello.arity = 1 @@ -37,9 +39,9 @@ def main(): registerForeign(hello) prolog = Prolog() prolog.assertz("father(michael,john)") - prolog.assertz("father(michael,gina)") + prolog.assertz("father(michael,gina)") list(prolog.query("father(michael,X), hello(X)")) - -if __name__ =="__main__": + +if __name__ == "__main__": main() diff --git a/examples/sendmoremoney/money.py b/examples/sendmoremoney/money.py index 146f85c..f70f661 100644 --- a/examples/sendmoremoney/money.py +++ b/examples/sendmoremoney/money.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/examples/sendmoremoney/money_new.py b/examples/sendmoremoney/money_new.py index 7b10348..8b63001 100644 --- a/examples/sendmoremoney/money_new.py +++ b/examples/sendmoremoney/money_new.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -38,7 +38,7 @@ def main(): prolog = Prolog() sendmore = Functor("sendmore") prolog.consult("money.pl") - + X = Variable() call(sendmore(X)) r = X.value @@ -46,6 +46,6 @@ def main(): print(letter, "=", r[i]) print("That's all...") + if __name__ == "__main__": main() - diff --git a/examples/sudoku/sudoku.py b/examples/sudoku/sudoku.py index f126b82..7108bfa 100644 --- a/examples/sudoku/sudoku.py +++ b/examples/sudoku/sudoku.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,38 +28,38 @@ _ = 0 puzzle1 = [ - [_,6,_,1,_,4,_,5,_], - [_,_,8,3,_,5,6,_,_], - [2,_,_,_,_,_,_,_,1], - [8,_,_,4,_,7,_,_,6], - [_,_,6,_,_,_,3,_,_], - [7,_,_,9,_,1,_,_,4], - [5,_,_,_,_,_,_,_,2], - [_,_,7,2,_,6,9,_,_], - [_,4,_,5,_,8,_,7,_] - ] + [_, 6, _, 1, _, 4, _, 5, _], + [_, _, 8, 3, _, 5, 6, _, _], + [2, _, _, _, _, _, _, _, 1], + [8, _, _, 4, _, 7, _, _, 6], + [_, _, 6, _, _, _, 3, _, _], + [7, _, _, 9, _, 1, _, _, 4], + [5, _, _, _, _, _, _, _, 2], + [_, _, 7, 2, _, 6, 9, _, _], + [_, 4, _, 5, _, 8, _, 7, _], +] puzzle2 = [ - [_,_,1,_,8,_,6,_,4], - [_,3,7,6,_,_,_,_,_], - [5,_,_,_,_,_,_,_,_], - [_,_,_,_,_,5,_,_,_], - [_,_,6,_,1,_,8,_,_], - [_,_,_,4,_,_,_,_,_], - [_,_,_,_,_,_,_,_,3], - [_,_,_,_,_,7,5,2,_], - [8,_,2,_,9,_,7,_,_] - ] + [_, _, 1, _, 8, _, 6, _, 4], + [_, 3, 7, 6, _, _, _, _, _], + [5, _, _, _, _, _, _, _, _], + [_, _, _, _, _, 5, _, _, _], + [_, _, 6, _, 1, _, 8, _, _], + [_, _, _, 4, _, _, _, _, _], + [_, _, _, _, _, _, _, _, 3], + [_, _, _, _, _, 7, 5, 2, _], + [8, _, 2, _, 9, _, 7, _, _], +] def pretty_print(table): - print("".join(["/---", "----"*8, "\\"])) + print("".join(["/---", "----" * 8, "\\"])) for row in table: print("".join(["|", "|".join(" %s " % (i or " ") for i in row), "|"])) - print("".join(["\\---", "----"*8, "/"])) + print("".join(["\\---", "----" * 8, "/"])) + - def solve(problem): prolog.consult("sudoku.pl") p = str(problem).replace("0", "_") @@ -70,7 +70,7 @@ def solve(problem): else: return False - + def main(): puzzle = puzzle1 print("-- PUZZLE --") @@ -83,7 +83,7 @@ def main(): else: print("This puzzle has no solutions [is it valid?]") - + if __name__ == "__main__": prolog = Prolog() main() diff --git a/examples/sudoku/sudoku_daily.py b/examples/sudoku/sudoku_daily.py index d0abffc..1b2a264 100644 --- a/examples/sudoku/sudoku_daily.py +++ b/examples/sudoku/sudoku_daily.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -45,7 +45,7 @@ def __init__(self): self.puzzle = [] self.__in_td = False HTMLParser.__init__(self) - + def handle_starttag(self, tag, attrs): if tag == "td": for attr in attrs: @@ -55,29 +55,29 @@ def handle_starttag(self, tag, attrs): elif tag == "input": if self.__in_td: self.puzzle.append(0) - + def handle_endtag(self, tag): if tag == "td": self.__in_td = False - + def handle_data(self, data): if self.__in_td: self.puzzle.append(int(data)) - + def pretty_print(table): - print("".join(["/---", "----"*8, "\\"])) + print("".join(["/---", "----" * 8, "\\"])) for row in table: print("".join(["|", "|".join(" %s " % (i or " ") for i in row), "|"])) - print("".join(["\\---", "----"*8, "/"])) + print("".join(["\\---", "----" * 8, "/"])) + - def get_daily_sudoku(url): puzzle = DailySudokuPuzzle() f = urllib_request.urlopen(url) puzzle.feed(f.read().decode("latin-1")) puzzle = puzzle.puzzle - return [puzzle[i*9:i*9+9] for i in range(9)] + return [puzzle[i * 9 : i * 9 + 9] for i in range(9)] def solve(problem): @@ -88,14 +88,14 @@ def solve(problem): result = result[0] return result["Puzzle"] else: - return False + return False if __name__ == "__main__": URL = "http://www.sudoku.org.uk/daily.asp" prolog = Prolog() # having this in `solve` bites! because of __del__ - print("Getting puzzle from:", URL) + print("Getting puzzle from:", URL) puzzle = get_daily_sudoku(URL) print("-- PUZZLE --") pretty_print(puzzle) @@ -106,4 +106,3 @@ def solve(problem): pretty_print(solution) else: print("This puzzle has no solutions [is it valid?]") - diff --git a/pyswip/core.py b/pyswip/core.py index b5b45c0..b0ada62 100644 --- a/pyswip/core.py +++ b/pyswip/core.py @@ -55,9 +55,9 @@ def _findSwiplPathFromFindLib(): {str, None} """ - path = (find_library('swipl') or - find_library('pl') or - find_library('libswipl')) # This last one is for Windows + path = ( + find_library("swipl") or find_library("pl") or find_library("libswipl") + ) # This last one is for Windows return path @@ -78,38 +78,37 @@ def _findSwiplFromExec(): fullName = None swiHome = None - try: # try to get library path from swipl executable. + try: # try to get library path from swipl executable. # We may have pl or swipl as the executable try: - cmd = Popen(['swipl', '--dump-runtime-variables'], stdout=PIPE) + cmd = Popen(["swipl", "--dump-runtime-variables"], stdout=PIPE) except OSError: - cmd = Popen(['pl', '--dump-runtime-variables'], stdout=PIPE) + cmd = Popen(["pl", "--dump-runtime-variables"], stdout=PIPE) ret = cmd.communicate() # Parse the output into a dictionary - ret = ret[0].decode().replace(';', '').splitlines() - ret = [line.split('=', 1) for line in ret] - rtvars = dict((name, value[1:-1]) for name, value in ret) # [1:-1] gets - # rid of the - # quotes - - if rtvars['PLSHARED'] == 'no': - raise ImportError('SWI-Prolog is not installed as a shared ' - 'library.') - else: # PLSHARED == 'yes' - swiHome = rtvars['PLBASE'] # The environment is in PLBASE + ret = ret[0].decode().replace(";", "").splitlines() + ret = [line.split("=", 1) for line in ret] + rtvars = dict((name, value[1:-1]) for name, value in ret) # [1:-1] gets + # rid of the + # quotes + + if rtvars["PLSHARED"] == "no": + raise ImportError("SWI-Prolog is not installed as a shared " "library.") + else: # PLSHARED == 'yes' + swiHome = rtvars["PLBASE"] # The environment is in PLBASE if not os.path.exists(swiHome): swiHome = None # determine platform specific path. First try runtime # variable `PLLIBSWIPL` introduced in 9.1.1/9.0.1 - if 'PLLIBSWIPL' in rtvars: - fullName = rtvars['PLLIBSWIPL'] + if "PLLIBSWIPL" in rtvars: + fullName = rtvars["PLLIBSWIPL"] # determine platform specific path elif platform == "win": - dllName = rtvars['PLLIB'][:-4] + '.' + rtvars['PLSOEXT'] - path = os.path.join(rtvars['PLBASE'], 'bin') + dllName = rtvars["PLLIB"][:-4] + "." + rtvars["PLSOEXT"] + path = os.path.join(rtvars["PLBASE"], "bin") fullName = os.path.join(path, dllName) if not os.path.exists(fullName): @@ -118,16 +117,16 @@ def _findSwiplFromExec(): elif platform == "cyg": # e.g. /usr/lib/pl-5.6.36/bin/i686-cygwin/cygpl.dll - dllName = 'cygpl.dll' - path = os.path.join(rtvars['PLBASE'], 'bin', rtvars['PLARCH']) + dllName = "cygpl.dll" + path = os.path.join(rtvars["PLBASE"], "bin", rtvars["PLARCH"]) fullName = os.path.join(path, dllName) if not os.path.exists(fullName): fullName = None elif platform == "dar": - dllName = 'lib' + rtvars['PLLIB'][2:] + '.' + "dylib" - path = os.path.join(rtvars['PLBASE'], 'lib', rtvars['PLARCH']) + dllName = "lib" + rtvars["PLLIB"][2:] + "." + "dylib" + path = os.path.join(rtvars["PLBASE"], "lib", rtvars["PLARCH"]) baseName = os.path.join(path, dllName) if os.path.exists(baseName): @@ -135,24 +134,24 @@ def _findSwiplFromExec(): else: # We will search for versions fullName = None - else: # assume UNIX-like + else: # assume UNIX-like # The SO name in some linuxes is of the form libswipl.so.5.10.2, # so we have to use glob to find the correct one - dllName = 'lib' + rtvars['PLLIB'][2:] + '.' + rtvars['PLSOEXT'] - path = os.path.join(rtvars['PLBASE'], 'lib', rtvars['PLARCH']) + dllName = "lib" + rtvars["PLLIB"][2:] + "." + rtvars["PLSOEXT"] + path = os.path.join(rtvars["PLBASE"], "lib", rtvars["PLARCH"]) baseName = os.path.join(path, dllName) if os.path.exists(baseName): fullName = baseName else: # We will search for versions - pattern = baseName + '.*' + pattern = baseName + ".*" files = glob.glob(pattern) if len(files) == 0: fullName = None else: fullName = files[0] - except (OSError, KeyError): # KeyError from accessing rtvars + except (OSError, KeyError): # KeyError from accessing rtvars pass return (fullName, swiHome) @@ -174,13 +173,12 @@ def _findSwiplWin(): ({str, None}, {str, None}) """ - dllNames = ('swipl.dll', 'libswipl.dll') + dllNames = ("swipl.dll", "libswipl.dll") # First try: check the usual installation path (this is faster but # hardcoded) - programFiles = os.getenv('ProgramFiles') - paths = [os.path.join(programFiles, r'pl\bin', dllName) - for dllName in dllNames] + programFiles = os.getenv("ProgramFiles") + paths = [os.path.join(programFiles, r"pl\bin", dllName) for dllName in dllNames] for path in paths: if os.path.exists(path): return (path, None) @@ -193,9 +191,10 @@ def _findSwiplWin(): # Third try: use reg.exe to find the installation path in the registry # (reg should be installed in all Windows XPs) try: - cmd = Popen(['reg', 'query', - r'HKEY_LOCAL_MACHINE\Software\SWI\Prolog', - '/v', 'home'], stdout=PIPE) + cmd = Popen( + ["reg", "query", r"HKEY_LOCAL_MACHINE\Software\SWI\Prolog", "/v", "home"], + stdout=PIPE, + ) ret = cmd.communicate() # Result is like: @@ -206,13 +205,12 @@ def _findSwiplWin(): # (Note: spaces may be \t or spaces in the output) ret = ret[0].splitlines() ret = [line.decode("utf-8") for line in ret if len(line) > 0] - pattern = re.compile('[^h]*home[^R]*REG_SZ( |\t)*(.*)$') + pattern = re.compile("[^h]*home[^R]*REG_SZ( |\t)*(.*)$") match = pattern.match(ret[-1]) if match is not None: path = match.group(2) - paths = [os.path.join(path, 'bin', dllName) - for dllName in dllNames] + paths = [os.path.join(path, "bin", dllName) for dllName in dllNames] for path in paths: if os.path.exists(path): return (path, None) @@ -233,6 +231,7 @@ def _findSwiplWin(): return (None, None) + def _findSwiplLin(): """ This function uses several heuristics to guess where SWI-Prolog is @@ -256,8 +255,15 @@ def _findSwiplLin(): return (path, swiHome) # Our last try: some hardcoded paths. - paths = ['/lib', '/usr/lib', '/usr/local/lib', '.', './lib', '/usr/lib/swi-prolog/lib/x86_64-linux'] - names = ['libswipl.so', 'libpl.so'] + paths = [ + "/lib", + "/usr/lib", + "/usr/local/lib", + ".", + "./lib", + "/usr/lib/swi-prolog/lib/x86_64-linux", + ] + names = ["libswipl.so", "libpl.so"] path = None for name in names: @@ -277,11 +283,11 @@ def walk(path, name): """ This function is a 2-time recursive func, that findin file in dirs - + :parameters: - `path` (str) - Directory path - `name` (str) - Name of file, that we lookin for - + :returns: Path to the swipl so, path to the resource file @@ -290,7 +296,7 @@ def walk(path, name): """ back_path = path[:] path = os.path.join(path, name) - + if os.path.exists(path): return path else: @@ -307,12 +313,12 @@ def walk(path, name): def get_swi_ver(): import re - swi_ver = input( - 'Please enter you SWI-Prolog version in format "X.Y.Z": ') - match = re.search(r'[0-9]+\.[0-9]+\.[0-9]+', swi_ver) + + swi_ver = input('Please enter you SWI-Prolog version in format "X.Y.Z": ') + match = re.search(r"[0-9]+\.[0-9]+\.[0-9]+", swi_ver) if match is None: - raise InputError('Error, type normal version') - + raise InputError("Error, type normal version") + return swi_ver @@ -320,10 +326,10 @@ def _findSwiplMacOSHome(): """ This function is guesing where SWI-Prolog is installed in MacOS via .app. - + :parameters: - `swi_ver` (str) - Version of SWI-Prolog in '[0-9].[0-9].[0-9]' format - + :returns: A tuple of (path to the swipl so, path to the resource file) @@ -333,16 +339,16 @@ def _findSwiplMacOSHome(): # Need more help with MacOS # That way works, but need more work - names = ['libswipl.dylib', 'libpl.dylib'] - - path = os.environ.get('SWI_HOME_DIR') + names = ["libswipl.dylib", "libpl.dylib"] + + path = os.environ.get("SWI_HOME_DIR") if path is None: - path = os.environ.get('SWI_LIB_DIR') + path = os.environ.get("SWI_LIB_DIR") if path is None: - path = os.environ.get('PLBASE') - if path is None: - path = '/Applications/SWI-Prolog.app/Contents/' - + path = os.environ.get("PLBASE") + if path is None: + path = "/Applications/SWI-Prolog.app/Contents/" + paths = [path] for name in names: @@ -350,7 +356,7 @@ def _findSwiplMacOSHome(): (path_res, back_path) = walk(path, name) if path_res is not None: - os.environ['SWI_LIB_DIR'] = back_path + os.environ["SWI_LIB_DIR"] = back_path return (path_res, None) return (None, None) @@ -379,8 +385,8 @@ def _findSwiplDar(): return (path, swiHome) # Last guess, searching for the file - paths = ['.', './lib', '/usr/lib/', '/usr/local/lib', '/opt/local/lib'] - names = ['libswipl.dylib', 'libpl.dylib'] + paths = [".", "./lib", "/usr/lib/", "/usr/local/lib", "/opt/local/lib"] + names = ["libswipl.dylib", "libpl.dylib"] for name in names: for path in paths: @@ -405,13 +411,13 @@ def _findSwipl(): :raises ImportError: If we cannot guess the name of the library """ # check environment - if 'LIBSWIPL_PATH' in os.environ: - return (os.environ['LIBSWIPL_PATH'], os.environ.get('SWI_HOME_DIR')) - - # Now begins the guesswork + if "LIBSWIPL_PATH" in os.environ: + return (os.environ["LIBSWIPL_PATH"], os.environ.get("SWI_HOME_DIR")) + + # Now begins the guesswork platform = sys.platform[:3] - if platform == "win": # In Windows, we have the default installer - # path and the registry to look + if platform == "win": # In Windows, we have the default installer + # path and the registry to look (path, swiHome) = _findSwiplWin() elif platform in ("lin", "cyg"): @@ -419,7 +425,7 @@ def _findSwipl(): elif platform == "dar": # Help with MacOS is welcome!! (path, swiHome) = _findSwiplDar() - + if path is None: (path, swiHome) = _findSwiplMacOSHome() @@ -429,9 +435,11 @@ def _findSwipl(): # This is a catch all raise if path is None: - raise ImportError('Could not find the SWI-Prolog library in this ' - 'platform. If you are sure it is installed, please ' - 'open an issue.') + raise ImportError( + "Could not find the SWI-Prolog library in this " + "platform. If you are sure it is installed, please " + "open an issue." + ) else: return (path, swiHome) @@ -446,18 +454,21 @@ def _fixWindowsPath(dll): - `dll` (str) - File name of the DLL """ - if sys.platform[:3] != 'win': - return # Nothing to do here + if sys.platform[:3] != "win": + return # Nothing to do here pathToDll = os.path.dirname(dll) - currentWindowsPath = os.getenv('PATH') + currentWindowsPath = os.getenv("PATH") if pathToDll not in currentWindowsPath: # We will prepend the path, to avoid conflicts between DLLs - newPath = pathToDll + ';' + currentWindowsPath - os.putenv('PATH', newPath) + newPath = pathToDll + ";" + currentWindowsPath + os.putenv("PATH", newPath) + _stringMap = {} + + def str_to_bytes(string): """ Turns a string into a bytes if necessary (i.e. if it is not already a bytes @@ -479,6 +490,7 @@ def str_to_bytes(string): return string + def list_to_bytes_list(strList): """ This function turns an array of strings into a pointer array @@ -497,14 +509,14 @@ def list_to_bytes_list(strList): return strList if not isinstance(strList, (list, set, tuple)): - raise TypeError("strList must be list, set or tuple, not " + - str(type(strList))) + raise TypeError("strList must be list, set or tuple, not " + str(type(strList))) pList = pList() for i, elem in enumerate(strList): pList[i] = str_to_bytes(elem) return pList + # create a decorator that turns the incoming strings into c_char_p compatible # butes or pointer arrays def check_strings(strings, arrays): @@ -526,10 +538,13 @@ def check_strings(strings, arrays): strings = [] # check if all entries are integers - for i,k in enumerate(strings): + for i, k in enumerate(strings): if not isinstance(k, int): - raise TypeError(('Wrong type for index at {0} '+ - 'in strings. Must be int, not {1}!').format(i,k)) + raise TypeError( + ( + "Wrong type for index at {0} " + "in strings. Must be int, not {1}!" + ).format(i, k) + ) # if given a single element, turn it into a list if isinstance(arrays, int): @@ -538,15 +553,20 @@ def check_strings(strings, arrays): arrays = [] # check if all entries are integers - for i,k in enumerate(arrays): + for i, k in enumerate(arrays): if not isinstance(k, int): - raise TypeError(('Wrong type for index at {0} '+ - 'in arrays. Must be int, not {1}!').format(i,k)) + raise TypeError( + ( + "Wrong type for index at {0} " + "in arrays. Must be int, not {1}!" + ).format(i, k) + ) # check if some index occurs in both if set(strings).intersection(arrays): - raise ValueError('One or more elements occur in both arrays and ' + - ' strings. One parameter cannot be both list and string!') + raise ValueError( + "One or more elements occur in both arrays and " + + " strings. One parameter cannot be both list and string!" + ) # create the checker that will check all arguments given by argsToCheck # and turn them into the right datatype. @@ -570,6 +590,7 @@ def check_and_call(*args): # Find the path and resource file. SWI_HOME_DIR shall be treated as a constant # by users of this module (_path, SWI_HOME_DIR) = _findSwipl() +SWI_HOME_DIR += "/lib/swipl" _fixWindowsPath(_path) @@ -580,13 +601,13 @@ def check_and_call(*args): # * VERSIONS * # *******************************/ -PL_VERSION_SYSTEM =1 # Prolog version -PL_VERSION_FLI =2 # PL_* compatibility -PL_VERSION_REC =3 # PL_record_external() compatibility -PL_VERSION_QLF =4 # Saved QLF format version -PL_VERSION_QLF_LOAD =5 # Min loadable QLF format version -PL_VERSION_VM =6 # VM signature -PL_VERSION_BUILT_IN =7 # Built-in predicate signature +PL_VERSION_SYSTEM = 1 # Prolog version +PL_VERSION_FLI = 2 # PL_* compatibility +PL_VERSION_REC = 3 # PL_record_external() compatibility +PL_VERSION_QLF = 4 # Saved QLF format version +PL_VERSION_QLF_LOAD = 5 # Min loadable QLF format version +PL_VERSION_VM = 6 # VM signature +PL_VERSION_BUILT_IN = 7 # Built-in predicate signature # After SWI-Prolog 8.5.2, PL_version was renamed to PL_version_info @@ -595,14 +616,14 @@ def check_and_call(*args): # https://github.com/SWI-Prolog/swipl-devel/issues/910 try: if hasattr(_lib, "PL_version_info"): - PL_version = _lib.PL_version_info # swi-prolog > 8.5.2 + PL_version = _lib.PL_version_info # swi-prolog > 8.5.2 else: - PL_version = _lib.PL_version # swi-prolog <= 8.5.2 + PL_version = _lib.PL_version # swi-prolog <= 8.5.2 PL_version.argtypes = [c_int] PL_version.restype = c_uint PL_VERSION = PL_version(PL_VERSION_SYSTEM) - if PL_VERSION<80200: + if PL_VERSION < 80200: raise Exception("swi-prolog >= 8.2.0 is required") except AttributeError: raise Exception("swi-prolog version number could not be determined") @@ -621,7 +642,7 @@ def check_and_call(*args): # /* PL_unify_term( arguments */ -if PL_VERSION<80200: +if PL_VERSION < 80200: # constants (from SWI-Prolog.h) # PL_unify_term() arguments PL_VARIABLE = 1 # nothing @@ -636,84 +657,84 @@ def check_and_call(*args): PL_CHARS = 12 # const char * PL_POINTER = 13 # void * # /* PlArg::PlArg(text, type) */ - #define PL_CODE_LIST (14) /* [ascii...] */ - #define PL_CHAR_LIST (15) /* [h,e,l,l,o] */ - #define PL_BOOL (16) /* PL_set_feature() */ - #define PL_FUNCTOR_CHARS (17) /* PL_unify_term() */ - #define _PL_PREDICATE_INDICATOR (18) /* predicate_t (Procedure) */ - #define PL_SHORT (19) /* short */ - #define PL_INT (20) /* int */ - #define PL_LONG (21) /* long */ - #define PL_DOUBLE (22) /* double */ - #define PL_NCHARS (23) /* unsigned, const char * */ - #define PL_UTF8_CHARS (24) /* const char * */ - #define PL_UTF8_STRING (25) /* const char * */ - #define PL_INT64 (26) /* int64_t */ - #define PL_NUTF8_CHARS (27) /* unsigned, const char * */ - #define PL_NUTF8_CODES (29) /* unsigned, const char * */ - #define PL_NUTF8_STRING (30) /* unsigned, const char * */ - #define PL_NWCHARS (31) /* unsigned, const wchar_t * */ - #define PL_NWCODES (32) /* unsigned, const wchar_t * */ - #define PL_NWSTRING (33) /* unsigned, const wchar_t * */ - #define PL_MBCHARS (34) /* const char * */ - #define PL_MBCODES (35) /* const char * */ - #define PL_MBSTRING (36) /* const char * */ - - REP_ISO_LATIN_1 = 0x0000 # output representation + # define PL_CODE_LIST (14) /* [ascii...] */ + # define PL_CHAR_LIST (15) /* [h,e,l,l,o] */ + # define PL_BOOL (16) /* PL_set_feature() */ + # define PL_FUNCTOR_CHARS (17) /* PL_unify_term() */ + # define _PL_PREDICATE_INDICATOR (18) /* predicate_t (Procedure) */ + # define PL_SHORT (19) /* short */ + # define PL_INT (20) /* int */ + # define PL_LONG (21) /* long */ + # define PL_DOUBLE (22) /* double */ + # define PL_NCHARS (23) /* unsigned, const char * */ + # define PL_UTF8_CHARS (24) /* const char * */ + # define PL_UTF8_STRING (25) /* const char * */ + # define PL_INT64 (26) /* int64_t */ + # define PL_NUTF8_CHARS (27) /* unsigned, const char * */ + # define PL_NUTF8_CODES (29) /* unsigned, const char * */ + # define PL_NUTF8_STRING (30) /* unsigned, const char * */ + # define PL_NWCHARS (31) /* unsigned, const wchar_t * */ + # define PL_NWCODES (32) /* unsigned, const wchar_t * */ + # define PL_NWSTRING (33) /* unsigned, const wchar_t * */ + # define PL_MBCHARS (34) /* const char * */ + # define PL_MBCODES (35) /* const char * */ + # define PL_MBSTRING (36) /* const char * */ + + REP_ISO_LATIN_1 = 0x0000 # output representation REP_UTF8 = 0x1000 REP_MB = 0x2000 else: - PL_VARIABLE = 1 # nothing - PL_ATOM = 2 # const char * - PL_INTEGER = 3 # int - PL_RATIONAL = 4 # rational number - PL_FLOAT = 5 # double - PL_STRING = 6 # const char * - PL_TERM = 7 - - PL_NIL = 8 # The constant [] - PL_BLOB = 9 # non-atom blob - PL_LIST_PAIR = 10 # [_|_] term + PL_VARIABLE = 1 # nothing + PL_ATOM = 2 # const char * + PL_INTEGER = 3 # int + PL_RATIONAL = 4 # rational number + PL_FLOAT = 5 # double + PL_STRING = 6 # const char * + PL_TERM = 7 + + PL_NIL = 8 # The constant [] + PL_BLOB = 9 # non-atom blob + PL_LIST_PAIR = 10 # [_|_] term # # PL_unify_term( - PL_FUNCTOR = 11 # functor_t, arg ... - PL_LIST = 12 # length, arg ... - PL_CHARS = 13 # const char * - PL_POINTER = 14 # void * + PL_FUNCTOR = 11 # functor_t, arg ... + PL_LIST = 12 # length, arg ... + PL_CHARS = 13 # const char * + PL_POINTER = 14 # void * # PlArg::PlArg(text, type - PL_CODE_LIST = 15 # [ascii...] - PL_CHAR_LIST = 16 # [h,e,l,l,o] - PL_BOOL = 17 # PL_set_prolog_flag( - PL_FUNCTOR_CHARS= 18 # PL_unify_term( - _PL_PREDICATE_INDICATOR= 19 # predicate_t= Procedure - PL_SHORT = 20 # short - PL_INT = 21 # int - PL_LONG = 22 # long - PL_DOUBLE = 23 # double - PL_NCHARS = 24 # size_t, const char * - PL_UTF8_CHARS = 25 # const char * - PL_UTF8_STRING = 26 # const char * - PL_INT64 = 27 # int64_t - PL_NUTF8_CHARS = 28 # size_t, const char * - PL_NUTF8_CODES = 29 # size_t, const char * - PL_NUTF8_STRING = 30 # size_t, const char * - PL_NWCHARS = 31 # size_t, const wchar_t * - PL_NWCODES = 32 # size_t, const wchar_t * - PL_NWSTRING = 33 # size_t, const wchar_t * - PL_MBCHARS = 34 # const char * - PL_MBCODES = 35 # const char * - PL_MBSTRING = 36 # const char * - PL_INTPTR = 37 # intptr_t - PL_CHAR = 38 # int - PL_CODE = 39 # int - PL_BYTE = 40 # int + PL_CODE_LIST = 15 # [ascii...] + PL_CHAR_LIST = 16 # [h,e,l,l,o] + PL_BOOL = 17 # PL_set_prolog_flag( + PL_FUNCTOR_CHARS = 18 # PL_unify_term( + _PL_PREDICATE_INDICATOR = 19 # predicate_t= Procedure + PL_SHORT = 20 # short + PL_INT = 21 # int + PL_LONG = 22 # long + PL_DOUBLE = 23 # double + PL_NCHARS = 24 # size_t, const char * + PL_UTF8_CHARS = 25 # const char * + PL_UTF8_STRING = 26 # const char * + PL_INT64 = 27 # int64_t + PL_NUTF8_CHARS = 28 # size_t, const char * + PL_NUTF8_CODES = 29 # size_t, const char * + PL_NUTF8_STRING = 30 # size_t, const char * + PL_NWCHARS = 31 # size_t, const wchar_t * + PL_NWCODES = 32 # size_t, const wchar_t * + PL_NWSTRING = 33 # size_t, const wchar_t * + PL_MBCHARS = 34 # const char * + PL_MBCODES = 35 # const char * + PL_MBSTRING = 36 # const char * + PL_INTPTR = 37 # intptr_t + PL_CHAR = 38 # int + PL_CODE = 39 # int + PL_BYTE = 40 # int # PL_skip_list( - PL_PARTIAL_LIST = 41 # a partial list - PL_CYCLIC_TERM = 42 # a cyclic list/term - PL_NOT_A_LIST = 43 # Object is not a list + PL_PARTIAL_LIST = 41 # a partial list + PL_CYCLIC_TERM = 42 # a cyclic list/term + PL_NOT_A_LIST = 43 # Object is not a list # dicts - PL_DICT = 44 + PL_DICT = 44 REP_ISO_LATIN_1 = 0x0000 # output representation REP_UTF8 = 0x00100000 @@ -735,7 +756,7 @@ def check_and_call(*args): PL_FA_NOTRACE = 0x01 # foreign cannot be traced PL_FA_TRANSPARENT = 0x02 # foreign is module transparent -PL_FA_NONDETERMINISTIC = 0x04 # foreign is non-deterministic +PL_FA_NONDETERMINISTIC = 0x04 # foreign is non-deterministic PL_FA_VARARGS = 0x08 # call using t0, ac, ctx PL_FA_CREF = 0x10 # Internal: has clause-reference */ @@ -745,7 +766,7 @@ def check_and_call(*args): PL_Q_DEBUG = 0x01 # = TRUE for backward compatibility PL_Q_NORMAL = 0x02 # normal usage -PL_Q_NODEBUG = 0x04 # use this one +PL_Q_NODEBUG = 0x04 # use this one PL_Q_CATCH_EXCEPTION = 0x08 # handle exceptions in C PL_Q_PASS_EXCEPTION = 0x10 # pass to parent environment PL_Q_DETERMINISTIC = 0x20 # call was deterministic @@ -754,14 +775,14 @@ def check_and_call(*args): # * BLOBS * # *******************************/ -#define PL_BLOB_MAGIC_B 0x75293a00 /* Magic to validate a blob-type */ -#define PL_BLOB_VERSION 1 /* Current version */ -#define PL_BLOB_MAGIC (PL_BLOB_MAGIC_B|PL_BLOB_VERSION) +# define PL_BLOB_MAGIC_B 0x75293a00 /* Magic to validate a blob-type */ +# define PL_BLOB_VERSION 1 /* Current version */ +# define PL_BLOB_MAGIC (PL_BLOB_MAGIC_B|PL_BLOB_VERSION) -#define PL_BLOB_UNIQUE 0x01 /* Blob content is unique */ -#define PL_BLOB_TEXT 0x02 /* blob contains text */ -#define PL_BLOB_NOCOPY 0x04 /* do not copy the data */ -#define PL_BLOB_WCHAR 0x08 /* wide character string */ +# define PL_BLOB_UNIQUE 0x01 /* Blob content is unique */ +# define PL_BLOB_TEXT 0x02 /* blob contains text */ +# define PL_BLOB_NOCOPY 0x04 /* do not copy the data */ +# define PL_BLOB_WCHAR 0x08 /* wide character string */ # /******************************* # * CHAR BUFFERS * @@ -779,7 +800,7 @@ def check_and_call(*args): CVT_ATOMIC = CVT_NUMBER | CVT_ATOM | CVT_STRING CVT_WRITE = 0x0040 # as of version 3.2.10 CVT_ALL = CVT_ATOMIC | CVT_LIST - CVT_MASK = 0x00ff + CVT_MASK = 0x00FF BUF_DISCARDABLE = 0x0000 BUF_RING = 0x0100 @@ -800,7 +821,7 @@ def check_and_call(*args): CVT_WRITE_CANONICAL = 0x00000080 CVT_WRITEQ = 0x000000C0 CVT_ALL = CVT_ATOMIC | CVT_LIST - CVT_MASK = 0x00000fff + CVT_MASK = 0x00000FFF BUF_DISCARDABLE = 0x00000000 BUF_STACK = 0x00010000 @@ -811,9 +832,6 @@ def check_and_call(*args): CVT_EXCEPTION = 0x00001000 # throw exception on error - - - argv = list_to_bytes_list(sys.argv + [None]) argc = len(sys.argv) @@ -865,6 +883,7 @@ def check_and_call(*args): PL_release_string_buffers_from_mark = _lib.PL_release_string_buffers_from_mark PL_release_string_buffers_from_mark.argtypes = [buf_mark_t] + @contextmanager def PL_STRINGS_MARK(): __PL_mark = buf_mark_t() @@ -874,6 +893,7 @@ def PL_STRINGS_MARK(): finally: PL_release_string_buffers_from_mark(__PL_mark) + PL_open_foreign_frame = _lib.PL_open_foreign_frame PL_open_foreign_frame.restype = fid_t @@ -924,38 +944,38 @@ def PL_STRINGS_MARK(): PL_put_list_chars = check_strings(1, None)(PL_put_list_chars) -#PL_EXPORT(void) PL_register_atom(atom_t a); +# PL_EXPORT(void) PL_register_atom(atom_t a); PL_register_atom = _lib.PL_register_atom PL_register_atom.argtypes = [atom_t] PL_register_atom.restype = None -#PL_EXPORT(void) PL_unregister_atom(atom_t a); +# PL_EXPORT(void) PL_unregister_atom(atom_t a); PL_unregister_atom = _lib.PL_unregister_atom PL_unregister_atom.argtypes = [atom_t] PL_unregister_atom.restype = None -#PL_EXPORT(atom_t) PL_functor_name(functor_t f); +# PL_EXPORT(atom_t) PL_functor_name(functor_t f); PL_functor_name = _lib.PL_functor_name PL_functor_name.argtypes = [functor_t] PL_functor_name.restype = atom_t -#PL_EXPORT(int) PL_functor_arity(functor_t f); +# PL_EXPORT(int) PL_functor_arity(functor_t f); PL_functor_arity = _lib.PL_functor_arity PL_functor_arity.argtypes = [functor_t] PL_functor_arity.restype = c_int # /* Get C-values from Prolog terms */ -#PL_EXPORT(int) PL_get_atom(term_t t, atom_t *a); +# PL_EXPORT(int) PL_get_atom(term_t t, atom_t *a); PL_get_atom = _lib.PL_get_atom PL_get_atom.argtypes = [term_t, POINTER(atom_t)] PL_get_atom.restype = c_int -#PL_EXPORT(int) PL_get_bool(term_t t, int *value); +# PL_EXPORT(int) PL_get_bool(term_t t, int *value); PL_get_bool = _lib.PL_get_bool PL_get_bool.argtypes = [term_t, POINTER(c_int)] PL_get_bool.restype = c_int -#PL_EXPORT(int) PL_get_atom_chars(term_t t, char **a); +# PL_EXPORT(int) PL_get_atom_chars(term_t t, char **a); PL_get_atom_chars = _lib.PL_get_atom_chars # FIXME PL_get_atom_chars.argtypes = [term_t, POINTER(c_char_p)] PL_get_atom_chars.restype = c_int @@ -1025,7 +1045,7 @@ def PL_STRINGS_MARK(): PL_predicate.argtypes = [c_char_p, c_int, c_char_p] PL_predicate.restype = predicate_t -PL_predicate = check_strings([0,2], None)(PL_predicate) +PL_predicate = check_strings([0, 2], None)(PL_predicate) PL_pred = _lib.PL_pred PL_pred.argtypes = [functor_t, module_t] @@ -1256,13 +1276,11 @@ def PL_STRINGS_MARK(): class _mbstate_t_value(Union): - _fields_ = [("__wch", wint_t), - ("__wchb", c_char * 4)] + _fields_ = [("__wch", wint_t), ("__wchb", c_char * 4)] class mbstate_t(Structure): - _fields_ = [("__count", c_int), - ("__value", _mbstate_t_value)] + _fields_ = [("__count", c_int), ("__value", _mbstate_t_value)] # stream related funcs @@ -1276,52 +1294,73 @@ class mbstate_t(Structure): # IOLOCK IOLOCK = c_void_p + # IOFUNCTIONS class IOFUNCTIONS(Structure): - _fields_ = [("read",Sread_function), - ("write",Swrite_function), - ("seek",Sseek_function), - ("close",Sclose_function), - ("seek64",Sseek64_function), - ("reserved",intptr_t*2)] + _fields_ = [ + ("read", Sread_function), + ("write", Swrite_function), + ("seek", Sseek_function), + ("close", Sclose_function), + ("seek64", Sseek64_function), + ("reserved", intptr_t * 2), + ] + # IOENC -ENC_UNKNOWN,ENC_OCTET,ENC_ASCII,ENC_ISO_LATIN_1,ENC_ANSI,ENC_UTF8,ENC_UNICODE_BE,ENC_UNICODE_LE,ENC_WCHAR = tuple(range(9)) +( + ENC_UNKNOWN, + ENC_OCTET, + ENC_ASCII, + ENC_ISO_LATIN_1, + ENC_ANSI, + ENC_UTF8, + ENC_UNICODE_BE, + ENC_UNICODE_LE, + ENC_WCHAR, +) = tuple(range(9)) IOENC = c_int + # IOPOS class IOPOS(Structure): - _fields_ = [("byteno",c_int64), - ("charno",c_int64), - ("lineno",c_int), - ("linepos",c_int), - ("reserved", intptr_t*2)] + _fields_ = [ + ("byteno", c_int64), + ("charno", c_int64), + ("lineno", c_int), + ("linepos", c_int), + ("reserved", intptr_t * 2), + ] + # IOSTREAM class IOSTREAM(Structure): - _fields_ = [("bufp",c_char_p), - ("limitp",c_char_p), - ("buffer",c_char_p), - ("unbuffer",c_char_p), - ("lastc",c_int), - ("magic",c_int), - ("bufsize",c_int), - ("flags",c_int), - ("posbuf",IOPOS), - ("position",POINTER(IOPOS)), - ("handle",c_void_p), - ("functions",IOFUNCTIONS), - ("locks",c_int), - ("mutex",IOLOCK), - ("closure_hook",CFUNCTYPE(None, c_void_p)), - ("closure",c_void_p), - ("timeout",c_int), - ("message",c_char_p), - ("encoding",IOENC)] -IOSTREAM._fields_.extend([("tee",IOSTREAM), - ("mbstate",POINTER(mbstate_t)), - ("reserved",intptr_t*6)]) - + _fields_ = [ + ("bufp", c_char_p), + ("limitp", c_char_p), + ("buffer", c_char_p), + ("unbuffer", c_char_p), + ("lastc", c_int), + ("magic", c_int), + ("bufsize", c_int), + ("flags", c_int), + ("posbuf", IOPOS), + ("position", POINTER(IOPOS)), + ("handle", c_void_p), + ("functions", IOFUNCTIONS), + ("locks", c_int), + ("mutex", IOLOCK), + ("closure_hook", CFUNCTYPE(None, c_void_p)), + ("closure", c_void_p), + ("timeout", c_int), + ("message", c_char_p), + ("encoding", IOENC), + ] + + +IOSTREAM._fields_.extend( + [("tee", IOSTREAM), ("mbstate", POINTER(mbstate_t)), ("reserved", intptr_t * 6)] +) Sopen_string = _lib.Sopen_string @@ -1334,7 +1373,8 @@ class IOSTREAM(Structure): PL_unify_stream = _lib.PL_unify_stream PL_unify_stream.argtypes = [term_t, POINTER(IOSTREAM)] -#create an exit hook which captures the exit code for our cleanup function + +# create an exit hook which captures the exit code for our cleanup function class ExitHook(object): def __init__(self): self.exit_code = None @@ -1348,18 +1388,20 @@ def exit(self, code=0): self.exit_code = code self._orig_exit(code) + _hook = ExitHook() _hook.hook() _isCleaned = False -#create a property for Atom's delete method in order to avoid segmentation fault +# create a property for Atom's delete method in order to avoid segmentation fault cleaned = property(_isCleaned) -#register the cleanup function to be executed on system exit + +# register the cleanup function to be executed on system exit @atexit.register def cleanupProlog(): # only do something if prolog has been initialised - if PL_is_initialised(None,None): + if PL_is_initialised(None, None): # clean up the prolog system using the caught exit code # if exit code is None, the program exits normally and we can use 0 diff --git a/pyswip/easy.py b/pyswip/easy.py index 8de5b8a..2b42d3a 100644 --- a/pyswip/easy.py +++ b/pyswip/easy.py @@ -28,7 +28,10 @@ # For backwards compability with Python 2 64bit if sys.version_info < (3,): - integer_types = (int, long,) + integer_types = ( + int, + long, + ) else: integer_types = (int,) @@ -44,6 +47,7 @@ class ArgumentTypeError(Exception): """ Thrown when an argument has the wrong type. """ + def __init__(self, expected, got): msg = "Expected an argument of type '%s' but got '%s'" % (expected, got) Exception.__init__(self, msg) @@ -66,7 +70,7 @@ def __init__(self, handleOrChars, chars=None): if chars is None: slen = c_size_t() self.chars = PL_atom_wchars(self.handle, byref(slen)) - else: # WA: PL_atom_wchars can fail to return correct string + else: # WA: PL_atom_wchars can fail to return correct string self.chars = chars def fromTerm(cls, term): @@ -80,6 +84,7 @@ def fromTerm(cls, term): a = atom_t() if PL_get_atom(term, byref(a)): return cls(a.value, getAtomChars(term)) + fromTerm = classmethod(fromTerm) def __del__(self): @@ -118,7 +123,7 @@ class Term(object): def __init__(self, handle=None, a0=None): if handle: - #self.handle = PL_copy_term_ref(handle) + # self.handle = PL_copy_term_ref(handle) self.handle = handle else: self.handle = PL_new_term_ref() @@ -144,9 +149,12 @@ def __hash__(self): # support unicode also in python 2 try: isinstance("", basestring) + def isstr(s): return isinstance(s, basestring) + except NameError: + def isstr(s): return isinstance(s, str) @@ -160,13 +168,13 @@ def __init__(self, handle=None, name=None): self.chars = name if handle: self.handle = handle - s = create_string_buffer(b"\00"*64) # FIXME: + s = create_string_buffer(b"\00" * 64) # FIXME: ptr = cast(s, c_char_p) - if PL_get_chars(handle, byref(ptr), CVT_VARIABLE|BUF_RING|REP_UTF8): + if PL_get_chars(handle, byref(ptr), CVT_VARIABLE | BUF_RING | REP_UTF8): self.chars = ptr.value else: self.handle = PL_new_term_ref() - #PL_put_variable(self.handle) + # PL_put_variable(self.handle) if (self.chars is not None) and not isinstance(self.chars, str): self.chars = self.chars.decode() @@ -195,8 +203,11 @@ def _fun(self, value, t): elif type(value) == list: fun = PL_unify_list else: - raise TypeError('Cannot unify {} with value {} due to the value unknown type {}'. - format(self, value, type(value))) + raise TypeError( + "Cannot unify {} with value {} due to the value unknown type {}".format( + self, value, type(value) + ) + ) if type(value) == list: a = PL_new_term_ref(self.handle) @@ -228,7 +239,7 @@ def __repr__(self): return "Variable(%s)" % self.handle def put(self, term): - #PL_put_variable(term) + # PL_put_variable(term) PL_put_term(term, self.handle) def __eq__(self, other): @@ -291,12 +302,13 @@ def fromTerm(cls, term): args.append(getTerm(a0 + i)) return cls(f.value, args=args, a0=a0) + fromTerm = classmethod(fromTerm) value = property(lambda s: s.__value) def __call__(self, *args): - assert self.arity == len(args) # FIXME: Put a decent error message + assert self.arity == len(args) # FIXME: Put a decent error message a = PL_new_term_refs(len(args)) for i, arg in enumerate(args): putTerm(a + i, arg) @@ -307,14 +319,18 @@ def __call__(self, *args): def __str__(self): if self.name is not None and self.arity is not None: - return "%s(%s)" % (self.name, - ', '.join([str(arg) for arg in self.args])) + return "%s(%s)" % (self.name, ", ".join([str(arg) for arg in self.args])) else: return self.__repr__() def __repr__(self): - return "".join(["Functor(", ",".join(str(x) for x - in [self.handle,self.arity]+self.args), ")"]) + return "".join( + [ + "Functor(", + ",".join(str(x) for x in [self.handle, self.arity] + self.args), + ")", + ] + ) def __eq__(self, other): if type(self) != type(other): @@ -329,9 +345,10 @@ def __hash__(self): def _unifier(arity, *args): assert arity == 2 try: - return {args[0].value:args[1].value} + return {args[0].value: args[1].value} except AttributeError: - return {args[0].value:args[1]} + return {args[0].value: args[1]} + _unify = Functor("=", 2) Functor.func[_unify.handle] = _unifier @@ -361,30 +378,27 @@ def putTerm(term, value): def putList(l, ls): PL_put_nil(l) for item in reversed(ls): - a = PL_new_term_ref() #PL_new_term_refs(len(ls)) + a = PL_new_term_ref() # PL_new_term_refs(len(ls)) putTerm(a, item) PL_cons_list(l, a, l) def getAtomChars(t): - """If t is an atom, return it as a string, otherwise raise InvalidTypeError. - """ + """If t is an atom, return it as a string, otherwise raise InvalidTypeError.""" s = c_char_p() - if PL_get_chars(t, byref(s), CVT_ATOM|REP_UTF8): + if PL_get_chars(t, byref(s), CVT_ATOM | REP_UTF8): return s.value else: raise InvalidTypeError("atom") def getAtom(t): - """If t is an atom, return it , otherwise raise InvalidTypeError. - """ + """If t is an atom, return it , otherwise raise InvalidTypeError.""" return Atom.fromTerm(t) def getBool(t): - """If t is of type bool, return it, otherwise raise InvalidTypeError. - """ + """If t is of type bool, return it, otherwise raise InvalidTypeError.""" b = c_int() if PL_get_long(t, byref(b)): return bool(b.value) @@ -393,8 +407,7 @@ def getBool(t): def getLong(t): - """If t is of type long, return it, otherwise raise InvalidTypeError. - """ + """If t is of type long, return it, otherwise raise InvalidTypeError.""" i = c_long() if PL_get_long(t, byref(i)): return i.value @@ -406,8 +419,7 @@ def getLong(t): def getFloat(t): - """If t is of type float, return it, otherwise raise InvalidTypeError. - """ + """If t is of type float, return it, otherwise raise InvalidTypeError.""" d = c_double() if PL_get_float(t, byref(d)): return d.value @@ -416,10 +428,9 @@ def getFloat(t): def getString(t): - """If t is of type string, return it, otherwise raise InvalidTypeError. - """ + """If t is of type string, return it, otherwise raise InvalidTypeError.""" s = c_char_p() - if PL_get_chars(t, byref(s), REP_UTF8|CVT_STRING): + if PL_get_chars(t, byref(s), REP_UTF8 | CVT_STRING): return s.value else: raise InvalidTypeError("string") @@ -487,8 +498,7 @@ def getList(x): def getFunctor(t): - """Return t as a functor - """ + """Return t as a functor""" return Functor.fromTerm(t) @@ -497,16 +507,17 @@ def getVariable(t): _getterm_router = { - PL_VARIABLE: getVariable, - PL_ATOM: getAtom, - PL_STRING: getString, - PL_INTEGER: getInteger, - PL_FLOAT: getFloat, - PL_TERM: getTerm, - } + PL_VARIABLE: getVariable, + PL_ATOM: getAtom, + PL_STRING: getString, + PL_INTEGER: getInteger, + PL_FLOAT: getFloat, + PL_TERM: getTerm, +} arities = {} + def _callbackWrapper(arity=1, nondeterministic=False): global arities @@ -528,6 +539,7 @@ def _foreignWrapper(fun, nondeterministic=False): res = funwraps.get(fun) if res is None: + def wrapper(*args): if nondeterministic: args = [getTerm(arg) for arg in args[:-1]] + [args[-1]] @@ -616,7 +628,7 @@ def __init__(self, *terms, **kwargs): if key not in ["flags", "module"]: raise Exception("Invalid kwarg: %s" % key, key) - flags = kwargs.get("flags", PL_Q_NODEBUG|PL_Q_CATCH_EXCEPTION) + flags = kwargs.get("flags", PL_Q_NODEBUG | PL_Q_CATCH_EXCEPTION) module = kwargs.get("module", None) t = terms[0] @@ -629,14 +641,17 @@ def __init__(self, *terms, **kwargs): def nextSolution(): return PL_next_solution(Query.qid) + nextSolution = staticmethod(nextSolution) def cutQuery(): PL_cut_query(Query.qid) + cutQuery = staticmethod(cutQuery) def closeQuery(): if Query.qid is not None: PL_close_query(Query.qid) Query.qid = None + closeQuery = staticmethod(closeQuery) diff --git a/pyswip/prolog.py b/pyswip/prolog.py index 5a3e677..f31a923 100644 --- a/pyswip/prolog.py +++ b/pyswip/prolog.py @@ -40,32 +40,40 @@ class NestedQueryError(PrologError): As this error may be somewhat difficult to debug in foreign code, it is automatically treated inside pySWIP """ + pass def _initialize(): args = [] args.append("./") - args.append("-q") # --quiet - args.append("--nosignals") # "Inhibit any signal handling by Prolog" + args.append("-q") # --quiet + args.append("--nosignals") # "Inhibit any signal handling by Prolog" if SWI_HOME_DIR is not None: args.append("--home=%s" % SWI_HOME_DIR) - result = PL_initialise(len(args),args) + result = PL_initialise(len(args), args) # result is a boolean variable (i.e. 0 or 1) indicating whether the # initialisation was successful or not. if not result: - raise PrologError("Could not initialize the Prolog environment." - "PL_initialise returned %d" % result) + raise PrologError( + "Could not initialize the Prolog environment." + "PL_initialise returned %d" % result + ) swipl_fid = PL_open_foreign_frame() swipl_load = PL_new_term_ref() - PL_chars_to_term("asserta(pyrun(GoalString,BindingList) :- " - "(atom_chars(A,GoalString)," - "atom_to_term(A,Goal,BindingList)," - "call(Goal))).", swipl_load) + PL_chars_to_term( + "asserta(pyrun(GoalString,BindingList) :- " + "(atom_chars(A,GoalString)," + "atom_to_term(A,Goal,BindingList)," + "call(Goal))).", + swipl_load, + ) PL_call(swipl_load, None) PL_discard_foreign_frame(swipl_fid) + + _initialize() @@ -99,14 +107,16 @@ def __call__(self, query, maxresult, catcherrors, normalize): swipl_goalCharList = swipl_args swipl_bindingList = swipl_args + 1 - PL_put_chars(swipl_goalCharList, PL_STRING|REP_UTF8, -1, query.encode("utf-8")) + PL_put_chars( + swipl_goalCharList, PL_STRING | REP_UTF8, -1, query.encode("utf-8") + ) swipl_predicate = PL_predicate("pyrun", 2, None) - plq = catcherrors and (PL_Q_NODEBUG|PL_Q_CATCH_EXCEPTION) or PL_Q_NORMAL + plq = catcherrors and (PL_Q_NODEBUG | PL_Q_CATCH_EXCEPTION) or PL_Q_NORMAL swipl_qid = PL_open_query(None, plq, swipl_predicate, swipl_args) - Prolog._queryIsOpen = True # From now on, the query will be considered open + Prolog._queryIsOpen = True # From now on, the query will be considered open try: while maxresult and PL_next_solution(swipl_qid): maxresult -= 1 @@ -128,10 +138,20 @@ def __call__(self, query, maxresult, catcherrors, normalize): if PL_exception(swipl_qid): term = getTerm(PL_exception(swipl_qid)) - raise PrologError("".join(["Caused by: '", query, "'. ", - "Returned: '", str(term), "'."])) - - finally: # This ensures that, whatever happens, we close the query + raise PrologError( + "".join( + [ + "Caused by: '", + query, + "'. ", + "Returned: '", + str(term), + "'.", + ] + ) + ) + + finally: # This ensures that, whatever happens, we close the query PL_cut_query(swipl_qid) PL_discard_foreign_frame(swipl_fid) Prolog._queryIsOpen = False @@ -197,17 +217,17 @@ def query(cls, query, maxresult=-1, catcherrors=True, normalize=True): def normalize_values(values): from pyswip.easy import Atom, Functor + if isinstance(values, Atom): return values.value if isinstance(values, Functor): normalized = str(values.name.value) if values.arity: - normalized_args = ([str(normalize_values(arg)) for arg in values.args]) - normalized = normalized + '(' + ', '.join(normalized_args) + ')' + normalized_args = [str(normalize_values(arg)) for arg in values.args] + normalized = normalized + "(" + ", ".join(normalized_args) + ")" return normalized elif isinstance(values, dict): return {key: normalize_values(v) for key, v in values.items()} elif isinstance(values, (list, tuple)): return [normalize_values(v) for v in values] return values - diff --git a/setup.py b/setup.py index 958c61d..8909b84 100644 --- a/setup.py +++ b/setup.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2020 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -29,27 +29,28 @@ long_description = f.read() -setup(name="pyswip", - version="0.2.11", - url="https://github.com/yuce/pyswip", - download_url="https://github.com/yuce/pyswip/releases", - author="Yuce Tekol", - author_email="yucetekol@gmail.com", - description="PySwip enables querying SWI-Prolog in your Python programs.", - long_description=long_description, - long_description_content_type="text/markdown", - license="MIT", - packages=["pyswip"], - keywords=["prolog", "artificial intelligence", "ai", "ffi", "ctypes"], - tests_require=['pytest', 'coverage', 'pytest-cov'], - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Topic :: Scientific/Engineering :: Artificial Intelligence', - 'Topic :: Software Development :: Libraries :: Python Modules' +setup( + name="pyswip", + version="0.3.0", + url="https://github.com/yuce/pyswip", + download_url="https://github.com/yuce/pyswip/releases", + author="Yuce Tekol", + author_email="yucetekol@gmail.com", + description="PySwip enables querying SWI-Prolog in your Python programs.", + long_description=long_description, + long_description_content_type="text/markdown", + license="MIT", + packages=["pyswip"], + keywords=["prolog", "artificial intelligence", "ai", "ffi", "ctypes"], + tests_require=["pytest", "coverage", "pytest-cov"], + classifiers=[ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries :: Python Modules", ], - ) +) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index df7bf8d..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pytest-cov==2.5.1 \ No newline at end of file diff --git a/tests/test_examples.py b/tests/test_examples.py index 74fb6d1..8418b0b 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -2,17 +2,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2018 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -37,33 +37,33 @@ class TestExamples(unittest.TestCase): WARNING: Since it is not possible to unload things from the Prolog base, the examples have to be 'orthogonal'. - + """ def test_create_term(self): """ Simple example of term creation. """ - + prolog = Prolog() - + a1 = PL_new_term_refs(2) a2 = a1 + 1 t = PL_new_term_ref() ta = PL_new_term_ref() - + animal2 = PL_new_functor(PL_new_atom("animal"), 2) assertz = PL_new_functor(PL_new_atom("assertz"), 1) - + PL_put_atom_chars(a1, "gnu") PL_put_integer(a2, 50) PL_cons_functor_v(t, animal2, a1) PL_cons_functor_v(ta, assertz, t) PL_call(ta, None) - + result = list(prolog.query("animal(X,Y)", catcherrors=True)) self.assertEqual(len(result), 1) - self.assertEqual(result[0], {'X': 'gnu', 'Y': 50}) + self.assertEqual(result[0], {"X": "gnu", "Y": 50}) def test_knowledgebase(self): """ @@ -71,30 +71,30 @@ def test_knowledgebase(self): """ p = Prolog() - + assertz = Functor("assertz") parent = Functor("parent", 2) test1 = newModule("test1") test2 = newModule("test2") - + call(assertz(parent("john", "bob")), module=test1) call(assertz(parent("jane", "bob")), module=test1) - + call(assertz(parent("mike", "bob")), module=test2) call(assertz(parent("gina", "bob")), module=test2) - + # Test knowledgebase module test1 - + result = set() X = Variable() q = Query(parent(X, "bob"), module=test1) while q.nextSolution(): - result.add(X.value.value) # X.value is an Atom + result.add(X.value.value) # X.value is an Atom q.closeQuery() self.assertEqual(result, set(["john", "jane"])) - + # Test knowledgebase module test2 - + result = set() q = Query(parent(X, "bob"), module=test2) while q.nextSolution(): @@ -108,36 +108,36 @@ def test_father(self): """ p = Prolog() - + father = Functor("father", 2) mother = Functor("mother", 2) - + p.assertz("father(john,mich)") p.assertz("father(john,gina)") p.assertz("mother(jane,mich)") - + X = Variable() Y = Variable() Z = Variable() - + result = [] q = Query(father("john", Y), mother(Z, Y)) while q.nextSolution(): y = Y.value.value z = Z.value.value - result.append({'Y': y, 'Z': z}) + result.append({"Y": y, "Z": z}) q.closeQuery() - + self.assertEqual(len(result), 1) - self.assertEqual(result[0], {'Y': 'mich', 'Z': 'jane'}) + self.assertEqual(result[0], {"Y": "mich", "Z": "jane"}) # Repeat the same query but using strings result = [] for s in p.query("father(john,Y),mother(Z,Y)"): result.append(s) self.assertEqual(len(result), 1) - self.assertEqual(result[0], {'Y': 'mich', 'Z': 'jane'}) - + self.assertEqual(result[0], {"Y": "mich", "Z": "jane"}) + def test_coins(self): """ Runs the coins example (uses clp library of SWI-Prolog). @@ -150,23 +150,23 @@ def test_coins(self): coins = Functor("coins", 3) S = Variable() q = Query(coins(S, count, total)) - + solutions = [] while q.nextSolution(): solutions.append(S.value) q.closeQuery() - + self.assertEqual(len(solutions), 105) # Now do the same test, but using the prolog.query interface - solutions = list(prolog.query("coins(S, %d, %d)." % (count,total))) + solutions = list(prolog.query("coins(S, %d, %d)." % (count, total))) self.assertEqual(len(solutions), 105) def test_draughts(self): """ Runs the draughts example (uses clp library of SWI-Prolog). """ - + prolog = Prolog() prolog.consult(example_path("draughts/puzzle1.pl")) solutions = [] @@ -177,29 +177,31 @@ def test_draughts(self): # Now do the same test, but using the prolog.query interface solutions = list(prolog.query("solve(B).")) self.assertEqual(len(solutions), 37) - + def test_hanoi(self): """ Runs the hanoi example. """ - + N = 3 # Number of disks - + result = [] + def notify(t): result.append((t[0].value, t[1].value)) + notify.arity = 1 - + prolog = Prolog() registerForeign(notify) prolog.consult(example_path("hanoi/hanoi.pl")) - list(prolog.query("hanoi(%d)" % N)) # Forces the query to run completely - + list(prolog.query("hanoi(%d)" % N)) # Forces the query to run completely + self.assertEqual(len(result), 7) - self.assertEqual(result[0], ('left', 'right')) - self.assertEqual(result[1], ('left', 'center')) - self.assertEqual(result[2], ('right', 'center')) - + self.assertEqual(result[0], ("left", "right")) + self.assertEqual(result[1], ("left", "center")) + self.assertEqual(result[2], ("right", "center")) + def test_sendmoremoney(self): """ Runs the sendmoremoney example:: @@ -208,11 +210,11 @@ def test_sendmoremoney(self): M O R E + ------- M O N E Y - + So, what should be the values of S, E, N, D, M, O, R, Y if they are all distinct digits. """ - + letters = "S E N D M O R Y".split() prolog = Prolog() sendmore = Functor("sendmore") @@ -226,42 +228,48 @@ def test_sendmoremoney(self): val[letter] = r[i] self.assertEqual(len(val), 8) - - send = val['S']*1e3 + val['E']*1e2 + val['N']*1e1 + val['D']*1e0 - more = val['M']*1e3 + val['O']*1e2 + val['R']*1e1 + val['E']*1e0 - money = val['M']*1e4 + val['O']*1e3 + val['N']*1e2 + val['E']*1e1 + val['Y']*1e0 + + send = val["S"] * 1e3 + val["E"] * 1e2 + val["N"] * 1e1 + val["D"] * 1e0 + more = val["M"] * 1e3 + val["O"] * 1e2 + val["R"] * 1e1 + val["E"] * 1e0 + money = ( + val["M"] * 1e4 + + val["O"] * 1e3 + + val["N"] * 1e2 + + val["E"] * 1e1 + + val["Y"] * 1e0 + ) self.assertEqual(money, send + more) - + def test_sudoku(self): """ Runs the sudoku example (uses clp library of SWI-Prolog). """ - + _ = 0 puzzle1 = [ - [_,6,_,1,_,4,_,5,_], - [_,_,8,3,_,5,6,_,_], - [2,_,_,_,_,_,_,_,1], - [8,_,_,4,_,7,_,_,6], - [_,_,6,_,_,_,3,_,_], - [7,_,_,9,_,1,_,_,4], - [5,_,_,_,_,_,_,_,2], - [_,_,7,2,_,6,9,_,_], - [_,4,_,5,_,8,_,7,_] - ] - + [_, 6, _, 1, _, 4, _, 5, _], + [_, _, 8, 3, _, 5, 6, _, _], + [2, _, _, _, _, _, _, _, 1], + [8, _, _, 4, _, 7, _, _, 6], + [_, _, 6, _, _, _, 3, _, _], + [7, _, _, 9, _, 1, _, _, 4], + [5, _, _, _, _, _, _, _, 2], + [_, _, 7, 2, _, 6, 9, _, _], + [_, 4, _, 5, _, 8, _, 7, _], + ] + puzzle2 = [ - [_,_,1,_,8,_,6,_,4], - [_,3,7,6,_,_,_,_,_], - [5,_,_,_,_,_,_,_,_], - [_,_,_,_,_,5,_,_,_], - [_,_,6,_,1,_,8,_,_], - [_,_,_,4,_,_,_,_,_], - [_,_,_,_,_,_,_,_,3], - [_,_,_,_,_,7,5,2,_], - [8,_,2,_,9,_,7,_,_] - ] - + [_, _, 1, _, 8, _, 6, _, 4], + [_, 3, 7, 6, _, _, _, _, _], + [5, _, _, _, _, _, _, _, _], + [_, _, _, _, _, 5, _, _, _], + [_, _, 6, _, 1, _, 8, _, _], + [_, _, _, 4, _, _, _, _, _], + [_, _, _, _, _, _, _, _, 3], + [_, _, _, _, _, 7, 5, 2, _], + [8, _, 2, _, 9, _, 7, _, _], + ] + prolog = Prolog() prolog.consult(example_path("sudoku/sudoku.pl")) @@ -272,8 +280,9 @@ def test_sudoku(self): # Does a simple check on the result result = result[0] for j, line in enumerate(result["L"]): - self.assertEqual(len(set(line)), 9, - "Failure in line %d: %s" % (j, line)) + self.assertEqual( + len(set(line)), 9, "Failure in line %d: %s" % (j, line) + ) else: self.fail("Failed while running example number %d" % i) @@ -285,12 +294,17 @@ def test_large_db(self): num_facts = 1250000 prolog = Prolog() for i in range(num_facts): - prolog.assertz('p(%s)' % i) + prolog.assertz("p(%s)" % i) - results = [r for r in prolog.query('p(I)')] + results = [r for r in prolog.query("p(I)")] self.assertEqual(len(results), num_facts) + def example_path(path): import os.path - return os.path.normpath(os.path.join(os.path.split(os.path.abspath(__file__))[0], "..", "examples", path)).replace("\\", "\\\\") + return os.path.normpath( + os.path.join( + os.path.split(os.path.abspath(__file__))[0], "..", "examples", path + ) + ).replace("\\", "\\\\") diff --git a/tests/test_foreign.py b/tests/test_foreign.py index 1f9f730..12a654c 100644 --- a/tests/test_foreign.py +++ b/tests/test_foreign.py @@ -1,6 +1,17 @@ import unittest -from pyswip import Prolog, registerForeign, PL_foreign_context, PL_foreign_control, PL_FIRST_CALL, PL_REDO, PL_PRUNED, PL_retry, PL_FA_NONDETERMINISTIC, Variable +from pyswip import ( + Prolog, + registerForeign, + PL_foreign_context, + PL_foreign_control, + PL_FIRST_CALL, + PL_REDO, + PL_PRUNED, + PL_retry, + PL_FA_NONDETERMINISTIC, + Variable, +) class MyTestCase(unittest.TestCase): @@ -16,9 +27,11 @@ def hello(t): prolog.assertz("father(michael,john)") prolog.assertz("father(michael,gina)") result = list(prolog.query("father(michael,X), hello(X)")) - self.assertEqual(len(result), 2, 'Query should return two results') - for name in ('john', 'gina'): - self.assertTrue({'X': name} in result, 'Expected result X:{} not present'.format(name)) + self.assertEqual(len(result), 2, "Query should return two results") + for name in ("john", "gina"): + self.assertTrue( + {"X": name} in result, "Expected result X:{} not present".format(name) + ) def test_nondeterministic_foreign(self): prolog = Prolog() @@ -39,22 +52,25 @@ def nondet(a, context): return PL_retry(context) elif control == PL_PRUNED: pass + nondet.arity = 1 registerForeign(nondet, flags=PL_FA_NONDETERMINISTIC) result = list(prolog.query("nondet(X)")) - self.assertEqual(len(result), 10, 'Query should return 10 results') + self.assertEqual(len(result), 10, "Query should return 10 results") for i in range(10): - self.assertTrue({'X': i} in result, 'Expected result X:{} not present'.format(i)) - + self.assertTrue( + {"X": i} in result, "Expected result X:{} not present".format(i) + ) + def test_atoms_and_strings_distinction(self): test_string = "string" def get_str(string): string.value = test_string - + def test_for_string(string, test_result): - test_result.value = (test_string == string.decode('utf-8')) + test_result.value = test_string == string.decode("utf-8") get_str.arity = 1 test_for_string.arity = 2 @@ -63,14 +79,18 @@ def test_for_string(string, test_result): registerForeign(test_for_string) prolog = Prolog() - + result = list(prolog.query("get_str(String), test_for_string(String, Result)")) - self.assertEqual(result[0]['Result'], 'true', 'A string return value should not be converted to an atom.') - + self.assertEqual( + result[0]["Result"], + "true", + "A string return value should not be converted to an atom.", + ) + def test_unifying_list_correctly(self): variable = Variable() variable.value = [1, 2] - self.assertEqual(variable.value, [1, 2], 'Lists should be unifyed correctly.') + self.assertEqual(variable.value, [1, 2], "Lists should be unifyed correctly.") def test_nested_lists(self): def get_list_of_lists(result): @@ -81,9 +101,12 @@ def get_list_of_lists(result): registerForeign(get_list_of_lists) prolog = Prolog() - + result = list(prolog.query("get_list_of_lists(Result)")) - self.assertTrue({'Result': [[1], [2]]} in result, 'Nested lists should be unified correctly as return value.') + self.assertTrue( + {"Result": [[1], [2]]} in result, + "Nested lists should be unified correctly as return value.", + ) def test_dictionary(self): prolog = Prolog() @@ -113,5 +136,5 @@ def test_nested_dictionary(self): ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_issues.py b/tests/test_issues.py index f05b344..515d44d 100644 --- a/tests/test_issues.py +++ b/tests/test_issues.py @@ -3,17 +3,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2012 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -32,7 +32,7 @@ class TestIssues(unittest.TestCase): """Each test method is named after the issue it is testing. The docstring - contains the link for the issue and the issue's description. + contains the link for the issue and the issue's description. """ def test_issue_13_17_and_6(self): @@ -52,8 +52,7 @@ def test_issue_13_17_and_6(self): """ # won't be very useful if it is not tested in several - # OSes - + # OSes def test_issue_1(self): """ @@ -61,13 +60,14 @@ def test_issue_1(self): Notes: This issue manifests only in 64bit stacks (note that a full 64 bit stack is needed. If running 32 in 64bit, it will not happen.) - + http://code.google.com/p/pyswip/issues/detail?id=1 """ # The simple code below should be enough to trigger the issue. As with # issue 13, if it does not work, it will segfault Python. from pyswip import Prolog + prolog = Prolog() prolog.assertz("randomTerm(michael,john)") @@ -81,8 +81,10 @@ def test_issue_8(self): from pyswip import Prolog, registerForeign callsToHello = [] + def hello(t): callsToHello.append(t) + hello.arity = 1 registerForeign(hello) @@ -91,24 +93,27 @@ def hello(t): prolog.assertz("parent(michael,john)") prolog.assertz("parent(michael,gina)") p = prolog.query("parent(michael,X), hello(X)") - result = list(p) # Will run over the iterator - + result = list(p) # Will run over the iterator + self.assertEqual(len(callsToHello), 2) # ['john', 'gina'] - self.assertEqual(len(result), 2) # [{'X': 'john'}, {'X': 'gina'}] + self.assertEqual(len(result), 2) # [{'X': 'john'}, {'X': 'gina'}] def test_issue_15(self): """ - sys.exit does not work when importing pyswip + sys.exit does not work when importing pyswip https://code.google.com/p/pyswip/issues/detail?id=15 """ # We will use it to test several return codes pythonExec = sys.executable + def runTestCode(code): - parameters = [pythonExec, - '-c', - 'import sys; import pyswip; sys.exit(%d)' % code,] + parameters = [ + pythonExec, + "-c", + "import sys; import pyswip; sys.exit(%d)" % code, + ] result = subprocess.call(parameters) self.assertEqual(result, code) @@ -119,7 +124,7 @@ def runTestCode(code): def test_issue_5(self): """ - Patch: hash and eq methods for Atom class. + Patch: hash and eq methods for Atom class. Ensures that the patch is working. @@ -128,9 +133,9 @@ def test_issue_5(self): from pyswip import Atom, Variable - a = Atom('test') - b = Atom('test2') - c = Atom('test') # Should be equal to a + a = Atom("test") + b = Atom("test2") + c = Atom("test") # Should be equal to a self.assertNotEqual(a, b) self.assertNotEqual(c, b) @@ -146,8 +151,8 @@ def test_issue_5(self): # The same semantics should be valid for other classes A = Variable() B = Variable() - C = Variable(A.handle) # This is equal to A - + C = Variable(A.handle) # This is equal to A + self.assertNotEqual(A, B) self.assertNotEqual(C, B) self.assertEqual(A, C) @@ -157,10 +162,10 @@ def test_issue_5(self): varSet.add(C) # This is equal to A self.assertEqual(len(varSet), 2) self.assertEqual(varSet, set([A, B])) - + def test_issue_4(self): """ - Patch for a dynamic method + Patch for a dynamic method Ensures that the patch is working. @@ -168,39 +173,39 @@ def test_issue_4(self): """ from pyswip import Prolog - - Prolog.dynamic('test_issue_4_d/1') - Prolog.assertz('test_issue_4_d(test1)') - Prolog.assertz('test_issue_4_d(test1)') - Prolog.assertz('test_issue_4_d(test1)') - Prolog.assertz('test_issue_4_d(test2)') - results = list(Prolog.query('test_issue_4_d(X)')) + + Prolog.dynamic("test_issue_4_d/1") + Prolog.assertz("test_issue_4_d(test1)") + Prolog.assertz("test_issue_4_d(test1)") + Prolog.assertz("test_issue_4_d(test1)") + Prolog.assertz("test_issue_4_d(test2)") + results = list(Prolog.query("test_issue_4_d(X)")) self.assertEqual(len(results), 4) - - Prolog.retract('test_issue_4_d(test1)') - results = list(Prolog.query('test_issue_4_d(X)')) + + Prolog.retract("test_issue_4_d(test1)") + results = list(Prolog.query("test_issue_4_d(X)")) self.assertEqual(len(results), 3) - - Prolog.retractall('test_issue_4_d(test1)') - results = list(Prolog.query('test_issue_4_d(X)')) + + Prolog.retractall("test_issue_4_d(test1)") + results = list(Prolog.query("test_issue_4_d(X)")) self.assertEqual(len(results), 1) def test_issue_3(self): """ - Problem with variables in lists + Problem with variables in lists https://code.google.com/p/pyswip/issues/detail?id=3 """ from pyswip import Prolog, Functor, Variable, Atom - + p = Prolog() - - f = Functor('f', 1) + + f = Functor("f", 1) A = Variable() B = Variable() C = Variable() - + x = f([A, B, C]) x = Functor.fromTerm(x) args = x.args[0] @@ -214,7 +219,7 @@ def test_issue_3(self): self.assertFalse(A == C, "Var B equals var C") # A more complex test - x = f([A, B, 'c']) + x = f([A, B, "c"]) x = Functor.fromTerm(x) args = x.args[0] self.assertEqual(type(args[0]), Variable) @@ -228,8 +233,10 @@ def test_issue_3(self): self.assertEqual(type(args[0]), Variable) self.assertEqual(type(args[1]), Variable) self.assertEqual(type(args[2]), Variable) - self.assertTrue(args[0] == args[2], "The first and last var of " - "f([A, B, A]) should be the same") + self.assertTrue( + args[0] == args[2], + "The first and last var of " "f([A, B, A]) should be the same", + ) def test_issue_62(self): """ @@ -243,14 +250,12 @@ def test_issue_62(self): prolog.consult("tests/test_unicode.pl", catcherrors=True) atoms = list(prolog.query("unicode_atom(B).")) - self.assertEqual(len(atoms), 3, - "Query should return exactly three atoms") + self.assertEqual(len(atoms), 3, "Query should return exactly three atoms") strings = list(prolog.query("unicode_string(B).")) - self.assertEqual(len(strings), 1, - "Query should return exactly one string") - self.assertEqual(strings[0]['B'], b'\xd1\x82\xd0\xb5\xd1\x81\xd1\x82') + self.assertEqual(len(strings), 1, "Query should return exactly one string") + self.assertEqual(strings[0]["B"], b"\xd1\x82\xd0\xb5\xd1\x81\xd1\x82") def test_functor_return(self): """ @@ -262,9 +267,9 @@ def test_functor_return(self): Not a formal issue, but see forum topic: https://groups.google.com/forum/#!topic/pyswip/Mpnfq4DH-mI """ - + import pyswip.prolog as pl - + p = pl.Prolog() p.consult("tests/test_functor_return.pl", catcherrors=True) query = "sentence(Parse_tree, [the,bat,eats,a,cat], [])" @@ -272,9 +277,8 @@ def test_functor_return(self): # This should not throw an exception results = list(p.query(query)) - self.assertEqual(len(results), 1, - "Query should return exactly one result") - + self.assertEqual(len(results), 1, "Query should return exactly one result") + ptree = results[0]["Parse_tree"] self.assertEqual(ptree, expectedTree) @@ -286,7 +290,6 @@ def test_functor_return(self): p.assertz("father(son(miki),kur)") p.assertz("father(son(kiwi),kur)") p.assertz("father(son(wiki),kur)") - - soln = [s["Y"] for s in p.query("friend(john,Y), father(Y,kur)", - maxresult=1)] + + soln = [s["Y"] for s in p.query("friend(john,Y), father(Y,kur)", maxresult=1)] self.assertEqual(soln[0], "son(miki)") diff --git a/tests/test_prolog.py b/tests/test_prolog.py index 31c506f..6d79d75 100644 --- a/tests/test_prolog.py +++ b/tests/test_prolog.py @@ -3,17 +3,17 @@ # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2012 Yüce Tekol -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -73,16 +73,18 @@ def test_nested_queries(self): def test_prolog_functor_in_list(self): p = pl.Prolog() - p.assertz('f([g(a,b),h(a,b,c)])') - self.assertEqual([{"L": [u"g(a, b)", u"h(a, b, c)"]}], list(p.query("f(L)"))) + p.assertz("f([g(a,b),h(a,b,c)])") + self.assertEqual([{"L": ["g(a, b)", "h(a, b, c)"]}], list(p.query("f(L)"))) p.retract("f([g(a,b),h(a,b,c)])") def test_prolog_functor_in_functor(self): p = pl.Prolog() p.assertz("f([g([h(a,1), h(b,1)])])") - self.assertEqual([{'G': [u"g(['h(a, 1)', 'h(b, 1)'])"]}], list(p.query('f(G)'))) + self.assertEqual([{"G": ["g(['h(a, 1)', 'h(b, 1)'])"]}], list(p.query("f(G)"))) p.assertz("a([b(c(x), d([y, z, w]))])") - self.assertEqual([{'B': [u"b(c(x), d(['y', 'z', 'w']))"]}], list(p.query('a(B)'))) + self.assertEqual( + [{"B": ["b(c(x), d(['y', 'z', 'w']))"]}], list(p.query("a(B)")) + ) p.retract("f([g([h(a,1), h(b,1)])])") p.retract("a([b(c(x), d([y, z, w]))])") @@ -102,7 +104,9 @@ def test_quoted_strings(self): self.assertEqual([{"X": b"a"}], list(p.query('X = "a"'))) p.assertz('test_quoted_strings("hello","world")') - self.assertEqual([{"A": b"hello", "B": b"world"}], list(p.query('test_quoted_strings(A,B)'))) + self.assertEqual( + [{"A": b"hello", "B": b"world"}], list(p.query("test_quoted_strings(A,B)")) + ) def test_prolog_read_file(self): """