diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml
new file mode 100644
index 0000000..ae03cd2
--- /dev/null
+++ b/.github/workflows/tox.yml
@@ -0,0 +1,25 @@
+name: tox
+
+on:
+ pull_request:
+ workflow_dispatch: # you can trigger this workflow manually
+
+jobs:
+ tox_on_ubuntu:
+
+ runs-on: ubuntu-22.04
+ strategy:
+ matrix:
+ python: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.python }}
+ - name: install tox
+ run: pip install tox
+ - name: run tox
+ # Run tox using the version of Python in `PATH`
+ run: tox -e py
diff --git a/.gitignore b/.gitignore
index 77e8b94..059c9f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,9 @@ __pycache__/
*.pdf
*.svg
+# experimental code:
+experimental/
+
# Mac OS
.DS_Store
@@ -26,7 +29,3 @@ htmlcov/
# due to vscode:
.vscode/
-
-# other
-bug_reports/
-experimental/
diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..ea5ba91
--- /dev/null
+++ b/docs/CODE_OF_CONDUCT.md
@@ -0,0 +1,16 @@
+# Code of Conduct
+
+Everyone interacting in this project is expected to follow the [PSF Code of
+Conduct]. This includes all infrastructure used in the development, such as
+codebases, issue trackers, chat rooms, and mailing lists.
+
+In general, this means that everyone is expected to be **open**,
+**considerate**, and **respectful** of others no matter what their position is
+within the project.
+
+
+## Reporting
+
+All incidents should be reported by emailing github@tambora.ch.
+
+[PSF Code of Conduct]: https://www.python.org/psf/conduct/
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
new file mode 100644
index 0000000..8bafcd3
--- /dev/null
+++ b/docs/CONTRIBUTING.md
@@ -0,0 +1,50 @@
+# How to contribute
+
+Thank for considering a contribution! Any help is greatly appreciated.
+
+
+## Did you find an issue?
+
+* Check whether your issue has already been reported by searching under
+ [existing issues](https://github.com/adrianschlatter/ppf.jabref/issues).
+
+* If don't find an issue addressing the problem, open a new issue.
+
+* Choose a meaningful title, describe clearly what you consider to be a
+ problem.
+
+* If possible, provide example code or other means to make it easy for a
+ maintainer to reproduce your problem.
+
+
+## Contributing Code
+
+You want to help with a bug or contribute a new feature? The [development
+docs](./dev/DEV_DOCS.md) might help you along.
+
+You already have a solution for an issue or a new feature? All the better! A
+pull request ("PR") is what you want to do.
+
+* Open a new [pull-request](https://github.com/adrianschlatter/ppf.jabref/pulls)
+ with your patch.
+
+* Try to create PRs that address a specific issue/feature/topic.
+
+* Avoid PRs containing an assortment of unrelated fixes and features. Better
+ split it into separate PRs for each topic.
+
+* Clean up your code before creating a pull request: Remove code that you have
+ commented out for debugging, remove test code you have added.
+
+* Make sure the PR's description clearly describes the problem and your
+ solution. Include relevant issue numbers if appropriate.
+
+* You increase the chances of quick acceptance of your PR significantly if you
+ have taken measures to assure quality (such as writing and passing tests).
+
+
+## Final remarks
+
+Currently, this project is maintained in the spare time of a single person
+having a family and a job. If you do not get immediate feedback to your issue
+or pull request, please have some patience.
diff --git a/docs/README.md b/docs/README.md
index 932e442..2e4652c 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,5 +1,7 @@
# ppf.jabref
+
+
ppf.jabref provides a [python](https://www.python.org) interface to
[JabRef](https://www.jabref.org) SQL databases. It maps database relations
to python classes using [SQLAlchemy](https://www.sqlalchemy.org).
@@ -66,3 +68,37 @@ the database connection. The query then uses ppf.jabref's Entry class to
obtain all Entries (=references) in the JabRef database. The for-loop
shows how to access fields and uses the File class to find out where the
documents linked to this entry are stored.
+
+
+## Installation
+
+ppf.jabref is available via [pypi](https://pypi.org):
+
+```
+pip install ppf.jabref
+```
+
+
+## Still reading?
+
+If you read this far, you're probably not here for the first time. If you use
+and like this project, would you consider giving it a Github Star? (The button
+is at the top of this website.) If not, maybe you're interested in one of
+[my other projects](https://github.com/adrianschlatter/ppf.sample/blob/develop/docs/list_of_projects.md)?
+
+
+## Contributing
+
+Did you find a bug and would like to report it? Or maybe you've fixed it
+already or want to help fixing it? That's great! Please read
+[CONTRIBUTING](./CONTRIBUTING.md) to learn how to proceed.
+
+To help ascertain that contributing to this project is a pleasant experience,
+we have established a [code of conduct](./CODE_OF_CONDUCT.md). You can expect
+everyone to adhere to it, just make sure you do as well.
+
+
+## Change Log
+
+* 0.1.1: Fix sqlalchemy deprecation warning, docstrings
+* 0.1.0: Initial release
diff --git a/docs/README_pypi.md b/docs/README_pypi.md
index 484f54b..b7a885a 100644
--- a/docs/README_pypi.md
+++ b/docs/README_pypi.md
@@ -42,3 +42,18 @@ the database connection. The query then uses ppf.jabref's Entry class to
obtain all Entries (=references) in the JabRef database. The for-loop
shows how to access fields and uses the File class to find out where the
documents linked to this entry are stored.
+
+
+## Installation
+
+ppf.jabref is available via [pypi](https://pypi.org):
+
+```
+pip install ppf.jabref
+```
+
+
+## Change Log
+
+* 0.1.1: Fix sqlalchemy deprecation warning, docstrings
+* 0.1.0: Initial release
diff --git a/docs/dev/DEV_DOCS.md b/docs/dev/DEV_DOCS.md
new file mode 100644
index 0000000..6df9ed8
--- /dev/null
+++ b/docs/dev/DEV_DOCS.md
@@ -0,0 +1,29 @@
+# Development Docs
+
+This is the top-level development document. It provides an overview and links
+to more specific documents.
+
+
+## Getting Started
+
+To start developing, you will need:
+
+```
+pip install .[test]
+pip install .[dev]
+```
+
+(Note: Depending on your shell, you might need to escape '[' and ']'.)
+
+
+## Packaging
+
+This package inherits its structure, build- and distribution "mechanics"
+from [ppf.sample](https://github.com/adrianschlatter/ppf.sample). And
+improvements gained with this package are also meant to flow back into said
+template project.
+
+
+## Release Process
+
+"[Release-Process](./release-process.md)" describes how we do a release.
diff --git a/docs/dev/release_process.md b/docs/dev/release_process.md
new file mode 100644
index 0000000..cf1410b
--- /dev/null
+++ b/docs/dev/release_process.md
@@ -0,0 +1,82 @@
+# Release Process
+
+Make sure that you:
+
+* updated the changelog (/docs/README.md)
+* checked the README for pypi (/docs/README_pypi.md)
+
+
+## Pull-Request into master
+
+* Push changes to Github
+* Create PR into master
+* Github will run workflows (tox in our case)
+* Merge PR when all is well (merge commit, not squash or fast forward)
+
+A commit in master is a release. Pull the new commit and tag it:
+
+```shell
+git checkout master
+git tag vX.Y.Z
+```
+
+Then push the tag back to Github:
+
+```shell
+git push origin vX.Y.Z
+```
+
+Tagging is important because the version of the package we are going to build
+is derived from this tag.
+
+
+## Build
+
+From the root of the project folder, run
+
+```
+python3 -m build --wheel
+```
+
+This generates a wheel distribution in /dist.
+
+## Test-pypi
+
+Upload to test.pypi.org by (make sure the '*' selects only your package):
+
+```
+twine upload -r testpypi dist/*
+```
+
+Note: Your `~/.pypirc` has to specify a `[testpypi]` section providing your
+username and password.
+
+
+## pypi
+
+If everything worked as expected, run
+
+```
+twine upload dist/*
+```
+
+Note: Your `~/.pypirc` has to specify a `[pypi]` section providing your
+username and password.
+
+## Test Install
+
+Activate an environment that does not have a (development-) installation of
+this package. Run
+
+```
+pip install ppf.jabref
+```
+
+This should download and install the version you've just released.
+
+
+## GitHub
+
+Finally, go to github, find the new tag, click the ellipsis, click
+"create release". Enter the tag name as title and copy the commit message
+into the description.
diff --git a/pyproject.toml b/pyproject.toml
index 6ddb34b..8fd8d67 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,3 @@
[build-system]
-# These are the assumed default build requirements from pip:
-# https://pip.pypa.io/en/stable/reference/pip/#pep-517-and-518-support
-requires = ["setuptools>=40.8.0", "wheel"]
+requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta"
diff --git a/setup.cfg b/setup.cfg
index a0786eb..7026971 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,87 +1,71 @@
[metadata]
name = ppf-jabref
-# Versions should comply with PEP440. For a discussion on single-sourcing
-# the version across setup.py and the project code, see
-# https://packaging.python.org/en/latest/single_source_version.html
version = 0.1.0
description = Python tools to work with JabRef libraries
long_description = file: docs/README_pypi.md
long_description_content_type = text/markdown
-
-# The projects main homepage.
url = https://github.com/adrianschlatter/ppf.jabref/tree/master
-# Author details
author = Adrian Schlatter
-# Do *not* provide author_email here. Instead, provide email for
-# software / licensing / ... questions (centrally) on GitHub.
-# If email changes (e.g. discard because of too much spam),
-# adjust email in single place instead of in every package.
-# author_email = private@private.pr
-# Choose your license
license = MIT
-# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers =
- # How mature is this project? Common values are
- # Development Status :: 2 - Pre-Alpha
- # 3 - Alpha
- # 4 - Beta
- # 5 - Production/Stable
Development Status :: 2 - Pre-Alpha
- # Indicate who your project is intended for
Intended Audience :: Developers
Intended Audience :: Science/Research
-
Topic :: Utilities
- # Pick your license as you wish (should match "license" above)
License :: OSI Approved :: MIT License
- # Specify the Python versions you support here. In particular, ensure
- # that you indicate whether you support Python 2, Python 3 or both.
Programming Language :: Python :: 3
Operating System :: OS Independent
-
-# What does your project relate to?
keywords = jabref, python, sqlalchemy
[options]
-# You can just specify the packages manually here if your project is
-# simple. Or you can use find_packages().
package_dir =
= src
packages = find_namespace:
-# List run-time dependencies here. These will be installed by pip when
-# your project is installed. For an analysis of "install_requires" vs pips
-# requirements files see:
-# https://packaging.python.org/en/latest/requirements.html
+# no python3.5 because we need f-strings:
+python_requires = >=3.6
install_requires = sqlalchemy
[options.packages.find]
where = src
[options.extras_require]
-# List additional groups of dependencies here (e.g. development
-# dependencies). You can install these using the following syntax,
-# for example:
-# $ pip install -e .[dev,test]
-dev = check-manifest
+# List additional groups of dependencies here. You can install these using
+# pip install -e .[dev,test]
test =
- pytest
- twine
check-manifest
+ setuptools>=40.5.0
flake8
- coverage
+ pytest
+ pytest-cov
+ importlib_metadata;python_version<='3.8'
+dev =
+ tox
+ twine
-[check-manifest]
-ignore =
- tox.ini
+[tool:pytest]
+testpaths =
tests
- tests/**
- docs/**
+ docs
+addopts =
+ --cov=ppf.jabref
+ --cov-report=
+ --cov-fail-under=100
+ --doctest-glob=*.md
[flake8]
per-file-ignores =
# imported but unused, import *, undefined name:
__init__.py: F401, F403, F821
+filename =
+ */src/*.py
+ */docs/*.py
+ */tests/*.py
+ setup.py
-[tool:pytest]
-testpaths =
+[check-manifest]
+ignore =
+ tox.ini
tests
+ tests/**
+ docs/**
+
diff --git a/setup.py b/setup.py
index ed8c032..c260289 100644
--- a/setup.py
+++ b/setup.py
@@ -1,11 +1,5 @@
# -*- coding: utf-8 -*-
-"""
-A setuptools based setup module.
-
-See:
-https://packaging.python.org/en/latest/distributing.html
-https://github.com/pypa/sampleproject
-"""
+# required for backwards compatibility
import setuptools
diff --git a/src/ppf/jabref/__init__.py b/src/ppf/jabref/__init__.py
index 0268006..6641c03 100644
--- a/src/ppf/jabref/__init__.py
+++ b/src/ppf/jabref/__init__.py
@@ -1,12 +1,17 @@
"""
-pyjabref
+ppf.jabref
++++++
"""
-# flake8: noqa
+try:
+ from importlib_metadata import version
+except ImportError: # pragma: no cover
+ from importlib.metadata import version # pragma: no cover
# import every function, class, etc. that should be visible in the package
from .jabref import *
+__version__ = version(__name__)
+
del jabref
del utils
diff --git a/src/ppf/jabref/jabref.py b/src/ppf/jabref/jabref.py
index d1cdbf8..76a6bf2 100644
--- a/src/ppf/jabref/jabref.py
+++ b/src/ppf/jabref/jabref.py
@@ -8,7 +8,7 @@
from sqlalchemy import Integer, VARCHAR, Text
from sqlalchemy.orm import relationship
from sqlalchemy.orm.collections import attribute_mapped_collection
-from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
import re
from .utils import export
@@ -19,9 +19,11 @@
@export
def citationkey2counter(citationkey):
- # 'a': 0, 'b': 1, ..., 'z': 25
- # 'aa': 26, 'ab': 27
- # 'aaa': 26 + 26**2 + 0, 'aab': 26 + 26**2 + 1
+ """
+ 'a': 0, 'b': 1, ..., 'z': 25
+ 'aa': 26, 'ab': 27
+ 'aaa': 26 + 26**2 + 0, 'aab': 26 + 26**2 + 1
+ """
# Let n = len(citationkey).
# Then first count all keys used by keys of length