diff --git a/.githooks/pre-commit/pre-commit b/.githooks/pre-commit/pre-commit deleted file mode 100755 index fad6514..0000000 --- a/.githooks/pre-commit/pre-commit +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env sh -python setup.py test diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5181b64 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +# This configures updates via dependabot only for github actions. + +version: 2 +updates: +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: "monthly" + cooldown: + default-days: 5 + groups: + github-actions: + patterns: + - "*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..aeeb0c2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,57 @@ +name: Python package + +on: + push: + branches: + - master + pull_request: + workflow_dispatch: + +concurrency: + group: ${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + PIP_DISABLE_PIP_VERSION_CHECK: "1" + PIP_PROGRESS_BAR: "off" + +permissions: {} + +jobs: + tests: + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + permissions: + contents: read + strategy: + fail-fast: false + matrix: + python-version: + - '3.9' + - '3.10' + - '3.11' + - '3.12' + - '3.13' + - '3.14' + steps: + - uses: actions/checkout@v6.0.1 + with: + persist-credentials: false + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v6.1.0 + with: + python-version: ${{ matrix.python-version }} + + - name: Install package + run: | + python -m pip install --upgrade pip + python -m pip install .[dev] + + - name: Run tests + run: | + python -m pytest + + - name: Build Sphinx documentation + run: | + sphinx-build -b html --nitpicky --fail-on-warning --keep-going docs docs/_build/html diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml new file mode 100644 index 0000000..63f74a8 --- /dev/null +++ b/.github/workflows/pypi-publish.yml @@ -0,0 +1,67 @@ +name: Publish Python Package +# Publishes tO PyPI for releases created in GitHub UI +# Note: For draft status, publishing is not triggered. +# Builds a packages for new tags "v1.2.3" or "v1.2.3.something" on master + +on: + push: + tags: + # GitHub glob matching is limited [1]. So we can't define a pattern matching + # pep 440 version definition [N!]N(.N)*[{a|b|rc}N][.postN][.devN] + - 'v[0-9]+.[0-9]+.[0-9]+.?*' + release: + types: [published] + +permissions: {} + +jobs: + build: + name: Build Python 🐍 distributions 📦 for publishing + # Don't try to publish from forks + if: github.repository == 'NatLibFi/Skosify' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6.0.1 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@v6.1.0 + with: + python-version: 3.13 + + - name: Install hatch + run: pipx install hatch + + - name: Build source and wheel archives + run: hatch build + + - name: Store built distribution + uses: actions/upload-artifact@v6.0.0 + with: + name: distribution-files + path: dist/ + + pypi-publish: + name: Build and publish Python 🐍 package 📦 to PyPI and TestPyPI + needs: build + runs-on: ubuntu-latest + environment: + name: pypi-release + url: https://pypi.org/p/skosify + permissions: + id-token: write # this permission is mandatory for trusted publishing + steps: + - name: Download built distribution + uses: actions/download-artifact@v7.0.0 + with: + name: distribution-files + path: dist + + - name: Publish package 📦 to PyPI + if: github.event_name == 'release' + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + with: + verbose: true + +# [1] https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml deleted file mode 100644 index 9aa9711..0000000 --- a/.github/workflows/python-package.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Python package - -on: [push, pull_request] -jobs: - test-job: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.6, 3.7, 3.8, 3.9] - - 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 - run: | - python -m pip install --upgrade pip - python -m pip install . - - name: Test - run: | - python setup.py test - - build-and-publish-job: - needs: test-job - runs-on: ubuntu-20.04 - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.9 - uses: actions/setup-python@v2 - with: - python-version: '3.9' - - name: Build distribution - run: | - python -m pip install wheel - python setup.py sdist bdist_wheel - - name: Publish distribution - uses: pypa/gh-action-pypi-publish@release/v1.4 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.gitignore b/.gitignore index f887813..6bfad66 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,5 @@ coverage.xml # Sphinx documentation docs/_build/ +# Autogenerated version file +skosify/_version.py diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 585a64f..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include LICENSE CHANGELOG.rst pytest.ini diff --git a/Makefile b/Makefile index 5dc0ec7..04edbcc 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ .PHONY: test docs build test: - python setup.py test + pytest doc: rm -rf docs/_build $(MAKE) -C docs html build: - python setup.py bdist_wheel + hatch build diff --git a/README.rst b/README.rst index a7a4a9b..c0db11b 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ .. image:: https://badge.fury.io/py/skosify.svg :target: https://badge.fury.io/py/skosify.svg -.. image:: https://travis-ci.org/NatLibFi/Skosify.svg?branch=master - :target: https://travis-ci.org/NatLibFi/Skosify +.. image:: https://github.com/NatLibFi/Skosify/workflows/CI/badge.svg + :target: https://github.com/NatLibFi/Skosify/actions .. image:: https://readthedocs.org/projects/skosify/badge/?version=latest :target: http://skosify.rtfd.io/ .. image:: https://codecov.io/gh/NatLibFi/Skosify/branch/master/graph/badge.svg @@ -18,12 +18,60 @@ the SKOS specification and related best practices. Installation ============ -Skosify requires Python 3.6+. +Skosify requires Python 3.9+. + +If you only want to use the command line interface it is suggested to install +with `uv tool `_ +or `pipx `_. +Both simplify installing and managing python command line applications. + +.. code-block:: console + + uv tool install skosify + + +or + +.. code-block:: console + + pipx install skosify + + +Of course you also use pip to install as for any other Python package. .. code-block:: console pip install --upgrade skosify + +To help testing locally with multiple Python versions, a +`tox `_ configuration file is provided (tox.ini). + +.. code-block:: console + + uv tool install tox + +Execute `tox` in the root of the repo to run the full set of tests. +Optionally just run specific environments, for example only Python 3.12: + +.. code-block:: console + + tox -e py312 + +The package can be built locally with any PEP517-conform build tool, for example +`build `_, +`uv build `_ +or `hatch `_. +Of course, you need to have the build tool installed. +For example, to use hatch as in gh-actions, run: + +.. code-block:: console + + uvx hatch build + +This command uses `uvx `_ +to run hatch with out installing it permanently. + Usage ===== @@ -79,6 +127,7 @@ Author and Contributors - Jakob Voß - Dan Michael O. Heggø - Alex Kourijoki +- David Linke See also ======== diff --git a/docs/background.rst b/docs/background.rst index 8689067..ec8896a 100644 --- a/docs/background.rst +++ b/docs/background.rst @@ -20,10 +20,10 @@ Publications - Osma Suominen and Christian Mader: Assessing and Improving the Quality of SKOS Vocabularies. Journal on Data Semantics, vol. 3, no. 1, pp. 47-73, June, 2014 - (`PDF `_) + (`PDF `__) - Osma Suominen and Eero Hyvönen: Improving the Quality of SKOS Vocabularies with Skosify. Proceedings of the 18th International Conference on Knowledge Engineering and Knowledge Management (EKAW 2012), Springer-Verlag, Galway, Ireland, October, 2012 - (`PDF `_) + (`PDF `__) diff --git a/docs/conf.py b/docs/conf.py index 2e9924a..03d67e2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,12 +1,43 @@ -# -*- coding: utf-8 -*- +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +from importlib.metadata import version as pkg_version + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. import os import sys sys.path.insert(0, os.path.abspath('../')) -needs_sphinx = '1.1' +# -- Project information ----------------------------------------------------- -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode'] +# General information about the project. +project = u'Skosify' +title = u'Skosify Documentation' +copyright = u'2017, Osma Suominen & Jakob Voß' +author = u'Osma Suominen & Jakob Voß' +# full version, including alpha/beta/rc tags +release = pkg_version("skosify") +# Short version +version = ".".join(release.split(".")[:3]) + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', + 'sphinx.ext.viewcode', +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -20,20 +51,12 @@ # The master toctree document. master_doc = 'index' -# General information about the project. -project = u'Skosify' -title = u'Skosify Documentation' -copyright = u'2017, Osma Suominen & Jakob Voß' -author = u'Osma Suominen & Jakob Voß' -version = '2.0.1' # Use bumpversion to update -release = version - # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -46,6 +69,8 @@ # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False +# In --nitpick mode, ignore missing references to rdflib types +nitpick_ignore = [('py:class', 'Graph')] # -- Options for HTML output ---------------------------------------------- @@ -79,7 +104,6 @@ (master_doc, 'Skosify.tex', title, author, 'manual'), ] - # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples @@ -89,4 +113,6 @@ ] # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7426911 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,140 @@ +[build-system] +requires = ["hatchling>=1.27", "hatch-vcs"] +build-backend = "hatchling.build" + +[project] +# Storing project metadata in pyproject.toml https://peps.python.org/pep-0621/ +name = "skosify" +description = "SKOS converter for RDFS/OWL/SKOS vocabularies." +authors = [ + {name = "Osma Suominen", email = "osma.suominen@helsinki.fi"}, +] +license = "MIT" +license-files = [ "LICENSE" ] +readme = "README.rst" +requires-python = ">=3.9" + +keywords = ["SKOS", "vocabulary", "RDF"] + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", +] + +dependencies = [ + "rdflib", + "tomli>=1.1.0; python_version < '3.11'", +] + +dynamic = ["version"] + +[project.urls] +Homepage = "https://github.com/NatLibFi/Skosify" +Documentation = "http://skosify.readthedocs.io/" +Changelog = "https://github.com/NatLibFi/Skosify/blob/master/CHANGELOG.rst" + +[project.optional-dependencies] +tests = [ + "pytest", + "pytest-flake8", + "pytest-cov", +] +docs = [ + "sphinx", + "sphinx-rtd-theme", +] +dev = [ + # Recursively including the project's own optional dependencies requires pip>=21.2 + "skosify[tests,docs]", + "hatch", +] + +[project.scripts] +skosify = "skosify.cli:main" + +[tool.hatch.metadata] +# Hatch disallows direct references for dependencies by default. +# We need to enable them to allow installing NFDI4Cat's vocexcel from github. +allow-direct-references = true + +# Build targets for hatch are defined as sections within tool.hatch.build.targets: +[tool.hatch.build.targets.sdist] +include = [ + "/skosify", + "LICENSE", + "CHANGELOG.rst", +] + +[tool.hatch.build.targets.wheel] +packages = ["skosify"] + +# integrates git-tag-based versions into hatch, https://github.com/ofek/hatch-vcs +[tool.hatch.version] +source = "vcs" + +[tool.hatch.build.hooks.vcs] +version-file = "skosify/_version.py" + +[tool.pytest.ini_options] +# pytest configuration: +# https://docs.pytest.org/en/stable/customize.html + +# Sets directories to be searched for tests. +testpaths = [ + "test", + ] + +# Directories that are not visited by pytest collector: +norecursedirs = "dist build .tox .git .cache __pycache__ .venv" + +# Execute doctests in classes, functions, and test modules (pytest) +addopts = [ + "-ra", # show extra summary info for skipped, failed, etc. tests + "-v", # verbose + "--strict-config", # Raise an error instead of a warning for pytest config issues + "--strict-markers", # Treat typos in function markers as an error + "--flake8", # Run flake8 checks + "--cov=skosify", # Coverage for skosify + "--cov-report=xml", # XML coverage report + "--cov-report=term-missing", # Terminal report with missing lines + "--tb=short", # short traceback format + "--durations=10", # Show 10 slowest tests + "--durations-min=1.0", # Only show tests >1s +] + +[tool.coverage.run] +# https://coverage.readthedocs.io/en/latest/config.html +branch = true +parallel = true +source_pkgs = ["skosify"] +omit = [ + "**/skosify/_version.py", +] + +[tool.coverage.paths] +# Specify where coverage schould look for source files. +source = [ + "skosify", +] + +[tool.coverage.report] +# Show in report which lines are not covered +show_missing = false +# Any line of the source code that matches one of these regexes is excluded +# from being reported as missing. +exclude_lines = [ + # Have to re-enable the standard pragma + "pragma: no cover", + # Don't complain if tests don't hit defensive assertion code: + "raise AssertionError", + "raise NotImplementedError", + "return NotImplemented", + "if __name__ == .__main__.:", +] diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 1c9d394..0000000 --- a/pytest.ini +++ /dev/null @@ -1,11 +0,0 @@ -# We need to keep this in `pytest.ini` instead of `setup.cfg` -# because `pep8maxlinelength` is affected by -# https://github.com/pytest-dev/pytest/issues/567 - -[pytest] -addopts = - --verbose - --flake8 - --cov=skosify - --cov-report xml - --cov-report term-missing diff --git a/setup.cfg b/setup.cfg index 22bdad9..6deafc2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,17 +1,2 @@ -[bumpversion] -current_version = 2.3.0 -commit = True -tag = True - -[aliases] -test = pytest - -[bumpversion:file:setup.py] - -[bumpversion:file:skosify/__init__.py] - -[bdist_wheel] -universal = 1 - [flake8] max-line-length = 120 diff --git a/setup.py b/setup.py deleted file mode 100644 index ceef3ee..0000000 --- a/setup.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python -# encoding=utf-8 -from __future__ import print_function -import os -import sys - -try: - from setuptools import setup -except ImportError: - print("This package requires 'setuptools' to be installed.") - sys.exit(1) - -here = os.path.abspath(os.path.dirname(__file__)) -README = open(os.path.join(here, 'README.rst')).read() - -setup(name='skosify', - version='2.3.0', # Use bumpversion to update - description='SKOS converter for RDFS/OWL/SKOS vocabularies.', - long_description=README, - classifiers=[ - 'Programming Language :: Python', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - ], - keywords='rdf skos', - author='Osma Suominen', - author_email='osma.suominen@helsinki.fi', - url='https://github.com/NatLibFi/Skosify', - license='MIT', - install_requires=['rdflib'], - setup_requires=['rdflib>=4.2.2', 'pytest-runner>=2.9'], - tests_require=['pytest', 'pytest-flake8', 'pytest-cov', 'pytest-catchlog'], - packages=['skosify', 'skosify.rdftools'], - entry_points={'console_scripts': ['skosify=skosify.cli:main']} - ) diff --git a/skosify.py b/skosify.py deleted file mode 100755 index 4db6a20..0000000 --- a/skosify.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python - -"""Wrapper script to directly run skosify from source tree.""" - -from skosify.cli import main - -if __name__ == '__main__': - main() diff --git a/skosify/__init__.py b/skosify/__init__.py index e2cc314..9f759e6 100644 --- a/skosify/__init__.py +++ b/skosify/__init__.py @@ -1,7 +1,16 @@ -# encoding=utf-8 +from importlib.metadata import PackageNotFoundError, version + from .skosify import skosify from .config import config from . import infer, check -__version__ = '2.3.0' # Use bumpversion to update __all__ = ['skosify', 'config', 'infer', 'check'] + +try: + __version__ = version("skosify") +except PackageNotFoundError: # pragma: no cover + # package is not installed + try: + from ._version import version as __version__ + except ImportError: + __version__ = "0.0.0" \ No newline at end of file diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..9edbd88 --- /dev/null +++ b/tox.ini @@ -0,0 +1,36 @@ +[tox] +requires = tox>=4 +env_list = clean, py{39,310,311,312,313,314}, report +skip_missing_interpreters = true + +[testenv] +# Use coverage run for proper parallel data file handling +commands = coverage run -m pytest --no-cov +# pass_env can be removed here and below for tox > 4.32.0 (https://github.com/tox-dev/tox/issues/3639) +pass_env = + LOCALAPPDATA +deps = + pytest + pytest-cov + pytest-flake8 + coverage +depends = + py{39,310,311,312,313,314}: clean + +[testenv:report] +pass_env = + LOCALAPPDATA +deps = coverage +skip_install = true +depends = py{39,310,311,312,313,314} +commands = + coverage combine + coverage report + coverage html + +[testenv:clean] +pass_env = + LOCALAPPDATA +deps = coverage +skip_install = true +commands = coverage erase