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