From c6752ee8ed415ced3d191095d115bcabad91cccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C3=BCce=20Tekol?= Date: Fri, 30 Aug 2024 09:17:53 +0300 Subject: [PATCH] Some cleanup and adopt hatch and ruff (#167) Cleanup and hatch+ruff adoption * Cleanup of the project * Adopted hatch for test, coverage and other tasks * Adopdated ruff as the linter * Minimum supported Python is 3.8 --- .github/workflows/tests.yaml | 13 +++-- Makefile | 17 +++--- README.md | 32 +++++++----- dev-requirements.txt | 4 +- examples/father.py | 2 - examples/hanoi/hanoi.py | 7 +-- examples/hanoi/hanoi_simple.py | 2 +- examples/knowledgebase.py | 2 +- examples/register_foreign_simple.py | 2 +- examples/sudoku/sudoku_daily.py | 12 ++--- pyproject.toml | 80 +++++++++++++++++++++++++++++ pyswip/__init__.py | 2 +- pyswip/core.py | 7 +-- pyswip/easy.py | 2 +- pyswip/prolog.py | 16 +++--- setup.py | 56 -------------------- tests/test_examples.py | 3 +- tests/test_issues.py | 2 +- tests/test_prolog.py | 2 - 19 files changed, 134 insertions(+), 129 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d5bf902..6f96e43 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -12,12 +12,10 @@ on: jobs: run-tests: - name: "Run tests with Python ${{ matrix.python-version }} on ${{ matrix.os }}" - strategy: matrix: - python-version: [ '3.8', '3.12' ] + python-version: [ '3.8', '3.12', '3.13' ] os: [ "ubuntu-22.04", "ubuntu-24.04" ] fail-fast: false @@ -25,10 +23,10 @@ jobs: steps: - - uses: "actions/checkout@v2" + - uses: "actions/checkout@v4" - name: "Set up Python ${{ matrix.python-version }}" - uses: "actions/setup-python@v2" + uses: "actions/setup-python@v5" with: python-version: "${{ matrix.python-version }}" @@ -48,5 +46,6 @@ jobs: env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" run: | - make cover - coveralls + make check + make coverage + make upload-coverage diff --git a/Makefile b/Makefile index d517c81..0611933 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,23 @@ -.PHONY: build clean cover test upload +.PHONY: build clean coverage upload-coverage test upload build: - python setup.py sdist - python setup.py bdist_wheel --universal + hatch build -clean: +clean:coveralls rm -rf dist build pyswip.egg-info cover: - py.test tests --verbose --cov=pyswip + hatch run test:coverage + +upload-coverage: + hatch run upload-coverage:coveralls test: - py.test tests --verbose + hatch run test:all upload: twine upload dist/* check: - black --check . + ruff format --check + ruff check diff --git a/README.md b/README.md index 05d823b..ae1b700 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ ## Installing the Latest Version -The latest SWI-Prolog supported by Ubuntu 18.04 and 20.04 are 7.6.4. We generally want to support LTS releases of -Ubuntu. The compatibility of PySwip with 7.6.4 on master is broken, so we are not able to release a new version until -this is fixed. In the meantime, you can use the following to install PySwip from the master branch: +The latest SWI-Prolog supported by Ubuntu 22.04 are 9.0.4. +We generally want to support LTS releases of Ubuntu. +You can use the following to install PySwip from the master branch: ``` pip install git+https://github.com/yuce/pyswip@master#egg=pyswip @@ -19,9 +19,7 @@ pip install git+https://github.com/yuce/pyswip@master#egg=pyswip ## The End of Python 2 Support Python 2 has reached end of life on January 1st, 2020 as documented [here](https://www.python.org/doc/sunset-python-2/). -So, PySwip 0.2.10 will be the last version which officially supports Python 2. - -Do you still require Python 2 support? Let us know at: https://github.com/yuce/pyswip/issues/94 +So, PySwip 0.2.10 is the last version which officially supports Python 2. --- @@ -29,29 +27,36 @@ Do you still require Python 2 support? Let us know at: https://github.com/yuce/p See the [CHANGELOG](CHANGELOG.md). -**WARNING! PySwip has no Windows installers! If you are a Windows user, see [INSTALL](https://github.com/yuce/pyswip/blob/master/INSTALL.md#windows). There are some "free download" sites that claim to be hosting PySwip installers. DO NOT TRUST THEM!** +--- + +**WARNING! PySwip has no Windows installers! +If you are a Windows user, see [INSTALL](https://github.com/yuce/pyswip/blob/master/INSTALL.md#windows). +There are some "free download" sites that claim to be hosting PySwip installers. +DO NOT TRUST THEM!** + +--- Thanks to all [contributors](CONTRIBUTORS.txt). ## Introduction PySwip is a Python - SWI-Prolog bridge enabling to query [SWI-Prolog](http://www.swi-prolog.org) in your Python programs. -It features an (incomplete) SWI-Prolog foreign language interface, a utility class that makes it easy querying with Prolog and also a -Pythonic interface. +It features an (incomplete) SWI-Prolog foreign language interface, a utility class that makes it easy querying with Prolog and also a Pythonic interface. Since PySwip uses SWI-Prolog as a shared library and ctypes to access it, it doesn't require compilation to be installed. ## Requirements: -* Python 3.6 and higher. +* Python 3.8 and higher. * PyPy is currently not supported. -* SWI-Prolog 8.2 and higher. +* SWI-Prolog 9.0.4 and higher. * `libswipl` as a shared library. *This is the default on most platforms.* * Works on Linux, Windows, MacOS and FreeBSD. Should work on other POSIX. ## Install -**IMPORTANT: Make sure the SWI-Prolog architecture is the same as the Python architecture. If you are using a 64bit build of Python, use a 64bit build of SWI-Prolog, etc.** +**IMPORTANT: Make sure the SWI-Prolog architecture is the same as the Python architecture. +If you are using a 64bit build of Python, use a 64bit build of SWI-Prolog, etc.** See [INSTALL](INSTALL.md) for instructions. @@ -117,7 +122,6 @@ q.closeQuery() # Outputs: # Hello, john # Hello, gina - ``` The core functionality of `Prolog.query` is based on Nathan Denny's public domain prolog.py. @@ -217,7 +221,7 @@ If you would like to reference PySwip in a LaTeX document, you can use the provi ## License ``` -Copyright (c) 2007-2020 Yüce Tekol and PySwip contributors +Copyright (c) 2007-2024 Yüce Tekol and PySwip contributors 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 diff --git a/dev-requirements.txt b/dev-requirements.txt index 22f5e54..38f73a4 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,2 +1,2 @@ -black==24.4.2 -pytest-cov==5.0.0 +hatch==1.12.0 +ruff==0.6.2 \ No newline at end of file diff --git a/examples/father.py b/examples/father.py index d402f83..655b5c0 100644 --- a/examples/father.py +++ b/examples/father.py @@ -30,13 +30,11 @@ def main(): father = Functor("father", 2) mother = Functor("mother", 2) - assertz = Functor("assertz", 1) p.assertz("father(john,mich)") p.assertz("father(john,gina)") p.assertz("mother(jane,mich)") - X = Variable() Y = Variable() Z = Variable() diff --git a/examples/hanoi/hanoi.py b/examples/hanoi/hanoi.py index 9b683b0..93af1b1 100644 --- a/examples/hanoi/hanoi.py +++ b/examples/hanoi/hanoi.py @@ -25,12 +25,7 @@ from collections import deque from pyswip.prolog import Prolog -from pyswip.easy import getList, registerForeign - -try: - input = raw_input -except NameError: - pass +from pyswip.easy import registerForeign class Notifier: diff --git a/examples/hanoi/hanoi_simple.py b/examples/hanoi/hanoi_simple.py index 8720f1d..9b73fde 100644 --- a/examples/hanoi/hanoi_simple.py +++ b/examples/hanoi/hanoi_simple.py @@ -23,7 +23,7 @@ from __future__ import print_function from pyswip.prolog import Prolog -from pyswip.easy import getList, registerForeign +from pyswip.easy import registerForeign N = 3 # Number of disks diff --git a/examples/knowledgebase.py b/examples/knowledgebase.py index c463985..120e3d7 100644 --- a/examples/knowledgebase.py +++ b/examples/knowledgebase.py @@ -25,7 +25,7 @@ def main(): - p = Prolog() + _ = Prolog() assertz = Functor("assertz") parent = Functor("parent", 2) diff --git a/examples/register_foreign_simple.py b/examples/register_foreign_simple.py index b3619a1..edb818e 100644 --- a/examples/register_foreign_simple.py +++ b/examples/register_foreign_simple.py @@ -25,7 +25,7 @@ from __future__ import print_function from pyswip.prolog import Prolog -from pyswip.easy import registerForeign, getAtomChars +from pyswip.easy import registerForeign def hello(t): diff --git a/examples/sudoku/sudoku_daily.py b/examples/sudoku/sudoku_daily.py index 1b2a264..3ebf852 100644 --- a/examples/sudoku/sudoku_daily.py +++ b/examples/sudoku/sudoku_daily.py @@ -29,15 +29,9 @@ from pyswip.prolog import Prolog from pyswip.easy import * -try: - from html.parser import HTMLParser -except: - from HTMLParser import HTMLParser - -try: - import urllib.request as urllib_request -except ImportError: - import urllib as urllib_request +from html.parser import HTMLParser + +import urllib.request as urllib_request class DailySudokuPuzzle(HTMLParser): diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b687dac --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,80 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "pyswip" +dynamic = ["version"] +description = "PySwip enables querying SWI-Prolog in your Python programs." +readme = "README.md" +requires-python = ">=3.8" +license = "MIT" +authors = [ + { name = "Yuce Tekol", email = "yucetekol@gmail.com" }, +] +keywords = [ + "ai", + "artificial intelligence", + "ctypes", + "ffi", + "prolog", +] +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", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", +] + +[project.urls] +Download = "https://github.com/yuce/pyswip/releases" +Homepage = "https://github.com/yuce/pyswip" + +[tool.hatch.version] +path = "pyswip/__init__.py" + +[tool.hatch.build.targets.sdist] +include = [ + "/pyswip", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/foo"] + +[tool.hatch.envs.test] +dependencies = [ + "pytest", + "pytest-cov", +] + +[tool.hatch.envs.test.scripts] +all = "py.test tests --verbose" +coverage = "py.test tests --verbose --cov=pyswip" + +[tool.hatch.envs.upload-coverage] +dependencies = [ + "coveralls", +] + +[tool.hatch.envs.upload-coverage.scripts] +coveralls = "coveralls" + +[tool.hatch.envs.types] +extra-dependencies = [ + "mypy>=1.0.0", +] +[tool.hatch.envs.types.scripts] +check = "mypy --install-types --non-interactive {args:pyswip tests}" + +[tool.ruff.lint] +ignore = ["F403", "F405", "E721"] diff --git a/pyswip/__init__.py b/pyswip/__init__.py index 1c807ff..0c30678 100644 --- a/pyswip/__init__.py +++ b/pyswip/__init__.py @@ -26,5 +26,5 @@ # PySwip version __VERSION__ = "0.2.11" -from pyswip.prolog import Prolog +from pyswip.prolog import Prolog as Prolog from pyswip.easy import * diff --git a/pyswip/core.py b/pyswip/core.py index 20fd58d..de02a9a 100644 --- a/pyswip/core.py +++ b/pyswip/core.py @@ -79,12 +79,8 @@ def _findSwiplFromExec(): swiHome = None 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) - except OSError: - cmd = Popen(["pl", "--dump-runtime-variables"], stdout=PIPE) + cmd = Popen(["swipl", "--dump-runtime-variables"], stdout=PIPE) ret = cmd.communicate() # Parse the output into a dictionary @@ -1401,7 +1397,6 @@ def exit(self, code=0): def cleanupProlog(): # only do something if prolog has been initialised 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 # instead. diff --git a/pyswip/easy.py b/pyswip/easy.py index 2b42d3a..e751d73 100644 --- a/pyswip/easy.py +++ b/pyswip/easy.py @@ -375,7 +375,7 @@ def putTerm(term, value): raise Exception("Not implemented") -def putList(l, ls): +def putList(l, ls): # noqa: E741 PL_put_nil(l) for item in reversed(ls): a = PL_new_term_ref() # PL_new_term_refs(len(ls)) diff --git a/pyswip/prolog.py b/pyswip/prolog.py index b87bad0..664a9cd 100644 --- a/pyswip/prolog.py +++ b/pyswip/prolog.py @@ -23,8 +23,6 @@ # SOFTWARE. -import sys - from pyswip.core import * @@ -64,10 +62,11 @@ def _initialize(): 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))).", + """ + asserta(pyrun(GoalString,BindingList) :- + (read_term_from_atom(GoalString, Goal, [variable_names(BindingList)]), + call(Goal))). + """, swipl_load, ) PL_call(swipl_load, None) @@ -78,7 +77,7 @@ def _initialize(): # NOTE: This import MUST be after _initialize is called!! -from pyswip.easy import getTerm +from pyswip.easy import getTerm # noqa: E402 class Prolog: @@ -90,7 +89,6 @@ class Prolog: _queryIsOpen = False class _QueryWrapper(object): - def __init__(self): if Prolog._queryIsOpen: raise NestedQueryError("The last query was not closed") @@ -99,7 +97,6 @@ def __call__(self, query, maxresult, catcherrors, normalize): Prolog._init_prolog_thread() swipl_fid = PL_open_foreign_frame() - swipl_head = PL_new_term_ref() swipl_args = PL_new_term_refs(2) swipl_goalCharList = swipl_args swipl_bindingList = swipl_args + 1 @@ -117,7 +114,6 @@ def __call__(self, query, maxresult, catcherrors, normalize): try: while maxresult and PL_next_solution(swipl_qid): maxresult -= 1 - bindings = [] swipl_list = PL_copy_term_ref(swipl_bindingList) t = getTerm(swipl_list) if normalize: diff --git a/setup.py b/setup.py deleted file mode 100644 index b529b76..0000000 --- a/setup.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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 -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import io - -from setuptools import setup - -with io.open("README.md", encoding="utf-8") as f: - 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", - ], -) diff --git a/tests/test_examples.py b/tests/test_examples.py index 8418b0b..026a40d 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -70,7 +70,7 @@ def test_knowledgebase(self): Tests usage of modules. """ - p = Prolog() + _ = Prolog() assertz = Functor("assertz") parent = Functor("parent", 2) @@ -116,7 +116,6 @@ def test_father(self): p.assertz("father(john,gina)") p.assertz("mother(jane,mich)") - X = Variable() Y = Variable() Z = Variable() diff --git a/tests/test_issues.py b/tests/test_issues.py index 515d44d..6866046 100644 --- a/tests/test_issues.py +++ b/tests/test_issues.py @@ -199,7 +199,7 @@ def test_issue_3(self): from pyswip import Prolog, Functor, Variable, Atom - p = Prolog() + _ = Prolog() f = Functor("f", 1) A = Variable() diff --git a/tests/test_prolog.py b/tests/test_prolog.py index 9cf8dab..a3238e7 100644 --- a/tests/test_prolog.py +++ b/tests/test_prolog.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- - # pyswip -- Python SWI-Prolog bridge # Copyright (c) 2007-2012 Yüce Tekol # @@ -28,7 +27,6 @@ """ import unittest -import doctest import pyswip.prolog as pl # This implicitly tests library loading code