diff --git a/.gitignore b/.gitignore
index fe42cd9..0d18b0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
+*~
+*.pyc
+dist/
sqlalchemy_pervasive.egg-info
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..c899989
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,46 @@
+
+# Changelog
+All notable changes to sqlalchemy-pervasive will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+
+## v0.3.0 (2024-07-09)
+
+### Feat
+
+- switch from setup.cfg to pyproject.toml + hatchling
+
+## 0.2.2 (2023-06-01)
+
+* Replace ``setup.py`` contents with ``setup.cfg``.
+
+
+## 0.2.1 (2021-03-03)
+
+* Update author / homepage metadata.
+
+
+## 0.2.0 (2021-03-03)
+
+* Officially declare "Python 3 only".
+
+
+## 0.1.3 (2018-02-24)
+
+* Replace ``execfile()`` usage per python 3.
+
+
+## 0.1.2
+
+* Misc. housekeeping. Added manifest, transferred copyright, etc.
+
+
+## 0.1.1
+
+* Fixed boolean conditions in rendered SQL.
+
+
+## 0.1.0
+
+* Initial version.
diff --git a/CHANGES.rst b/CHANGES.rst
deleted file mode 100644
index b458c23..0000000
--- a/CHANGES.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-
-0.1.3 (2018-02-24)
-------------------
-
-* Replace ``execfile()`` usage per python 3.
-
-
-0.1.2
------
-
-* Misc. housekeeping. Added manifest, transferred copyright, etc.
-
-
-0.1.1
------
-
-* Fixed boolean conditions in rendered SQL.
-
-
-0.1.0
------
-
-* Initial version.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..83698bf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+
+# sqlalchemy-pervasive
+
+A SQLAlchemy dialect for the [Pervasive
+PSQL](http://www.pervasive.com/database/) database engine.
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 84479aa..0000000
--- a/README.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-
-sqlalchemy-pervasive
-====================
-
-A SQLAlchemy dialect for the `Pervasive PSQL`_ database engine.
-
-.. _Pervasive PSQL: http://www.pervasive.com/database/
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..4291bc3
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,52 @@
+
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+
+[project]
+name = "sqlalchemy-pervasive"
+version = "0.3.0"
+description = "SQLAlchemy Dialect for Pervasive PSQL"
+readme = "README.md"
+authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
+license = {text = "GNU GPL v3+"}
+classifiers = [
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
+ "Natural Language :: English",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+]
+dependencies = [
+ "pyodbc",
+ "SQLAlchemy",
+]
+
+
+[project.entry-points."sqlalchemy.dialects"]
+pervasive = "sqlalchemy_pervasive.pyodbc:PervasiveDialect_pyodbc"
+
+
+[project.urls]
+Homepage = "https://kallithea.rattailproject.org/rattail-project-contrib/sqlalchemy-pervasive"
+Repository = "https://kallithea.rattailproject.org/rattail-project-contrib/sqlalchemy-pervasive"
+Changelog = "https://kallithea.rattailproject.org/rattail-project-contrib/sqlalchemy-pervasive/files/master/CHANGELOG.md"
+
+
+[tool.commitizen]
+version_provider = "pep621"
+tag_format = "v$version"
+update_changelog_on_bump = true
+
+
+[tool.nosetests]
+nocapture = 1
+cover-package = "sqlalchemy_pervasive"
+cover-erase = 1
+cover-inclusive = 1
+cover-html = 1
+cover-html-dir = "htmlcov"
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index c07ec39..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,7 +0,0 @@
-[nosetests]
-nocapture = 1
-cover-package = sqlalchemy_pervasive
-cover-erase = 1
-cover-inclusive = 1
-cover-html = 1
-cover-html-dir = htmlcov
diff --git a/setup.py b/setup.py
deleted file mode 100644
index c013306..0000000
--- a/setup.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# sqlalchemy-pervasive -- SQLAlchemy Dialect for Pervasive PSQL
-# Copyright © 2013-2018 Sacramento Natural Foods Co-op, Inc
-#
-# This file is part of sqlalchemy-pervasive.
-#
-# sqlalchemy-pervasive is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or (at your
-# option) any later version.
-#
-# sqlalchemy-pervasive is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# sqlalchemy-pervasive. If not, see .
-#
-################################################################################
-
-from __future__ import unicode_literals, absolute_import
-
-import os.path
-from setuptools import setup, find_packages
-
-
-here = os.path.abspath(os.path.dirname(__file__))
-exec(open(os.path.join(here, 'sqlalchemy_pervasive', '_version.py')).read())
-
-README = open(os.path.join(here, 'README.rst')).read()
-CHANGES = open(os.path.join(here, 'CHANGES.rst')).read()
-
-
-requires = [
- #
- # Version numbers within comments below have specific meanings.
- # Basically the 'low' value is a "soft low," and 'high' a "soft high."
- # In other words:
- #
- # If either a 'low' or 'high' value exists, the primary point to be
- # made about the value is that it represents the most current (stable)
- # version available for the package (assuming typical public access
- # methods) whenever this project was started and/or documented.
- # Therefore:
- #
- # If a 'low' version is present, you should know that attempts to use
- # versions of the package significantly older than the 'low' version
- # may not yield happy results. (A "hard" high limit may or may not be
- # indicated by a true version requirement.)
- #
- # Similarly, if a 'high' version is present, and especially if this
- # project has laid dormant for a while, you may need to refactor a bit
- # when attempting to support a more recent version of the package. (A
- # "hard" low limit should be indicated by a true version requirement
- # when a 'high' version is present.)
- #
- # In any case, developers and other users are encouraged to play
- # outside the lines with regard to these soft limits. If bugs are
- # encountered then they should be filed as such.
- #
- # package # low high
-
- 'pyodbc', # 2.1.11
- 'SQLAlchemy', # 0.8.2
- ]
-
-
-setup(
- name = "sqlalchemy-pervasive",
- version = __version__,
- author = "Sacramento Natural Foods Co-op, Inc",
- author_email = "developer@sacfoodcoop.com",
- url = 'https://github.com/SacNaturalFoods/sqlalchemy-pervasive',
- license = "GNU GPL v3",
- description = "SQLAlchemy Dialect for Pervasive PSQL",
- long_description = README + '\n\n' + CHANGES,
-
- classifiers = [
- 'Development Status :: 3 - Alpha',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
- 'Natural Language :: English',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2.6',
- 'Programming Language :: Python :: 2.7',
- 'Topic :: Software Development :: Libraries :: Python Modules',
- ],
-
- install_requires = requires,
- packages = find_packages(exclude=['tests']),
- include_package_data = True,
-
- entry_points = """
-
-[sqlalchemy.dialects]
-pervasive = sqlalchemy_pervasive.pyodbc:PervasiveDialect_pyodbc
-
-""",
- )
diff --git a/sqlalchemy_pervasive/__init__.py b/sqlalchemy_pervasive/__init__.py
index dd6cf45..7d73f57 100644
--- a/sqlalchemy_pervasive/__init__.py
+++ b/sqlalchemy_pervasive/__init__.py
@@ -1,9 +1,8 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
+# -*- coding: utf-8; -*-
################################################################################
#
# sqlalchemy-pervasive -- SQLAlchemy Dialect for Pervasive PSQL
-# Copyright © 2013 Sacramento Natural Foods Co-op, Inc
+# Copyright © 2013-2021 Sacramento Natural Foods Co-op, Inc
#
# This file is part of sqlalchemy-pervasive.
#
diff --git a/sqlalchemy_pervasive/_version.py b/sqlalchemy_pervasive/_version.py
index 8ce9b36..3d8fab5 100644
--- a/sqlalchemy_pervasive/_version.py
+++ b/sqlalchemy_pervasive/_version.py
@@ -1 +1,6 @@
-__version__ = '0.1.3'
+# -*- coding: utf-8; -*-
+
+from importlib.metadata import version
+
+
+__version__ = version('sqlalchemy-pervasive')
diff --git a/sqlalchemy_pervasive/base.py b/sqlalchemy_pervasive/base.py
index 5a7ae7f..d0e63ce 100644
--- a/sqlalchemy_pervasive/base.py
+++ b/sqlalchemy_pervasive/base.py
@@ -1,9 +1,8 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
+# -*- coding: utf-8; -*-
################################################################################
#
# sqlalchemy-pervasive -- SQLAlchemy Dialect for Pervasive PSQL
-# Copyright © 2013 Sacramento Natural Foods Co-op, Inc
+# Copyright © 2013-2021 Sacramento Natural Foods Co-op, Inc
#
# This file is part of sqlalchemy-pervasive.
#
@@ -21,7 +20,6 @@
# sqlalchemy-pervasive. If not, see .
#
################################################################################
-
"""
Support for the Pervasive PSQL database engine.
"""
diff --git a/sqlalchemy_pervasive/pyodbc.py b/sqlalchemy_pervasive/pyodbc.py
index 824330e..54890a1 100644
--- a/sqlalchemy_pervasive/pyodbc.py
+++ b/sqlalchemy_pervasive/pyodbc.py
@@ -1,9 +1,8 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
+# -*- coding: utf-8; -*-
################################################################################
#
# sqlalchemy-pervasive -- SQLAlchemy Dialect for Pervasive PSQL
-# Copyright © 2013 Sacramento Natural Foods Co-op, Inc
+# Copyright © 2013-2021 Sacramento Natural Foods Co-op, Inc
#
# This file is part of sqlalchemy-pervasive.
#
@@ -21,7 +20,6 @@
# sqlalchemy-pervasive. If not, see .
#
################################################################################
-
"""
Support for Pervasive PSQL via ``pyodbc``.
"""
diff --git a/fabfile.py b/tasks.py
similarity index 73%
rename from fabfile.py
rename to tasks.py
index 3894324..707e130 100644
--- a/fabfile.py
+++ b/tasks.py
@@ -1,9 +1,8 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
+# -*- coding: utf-8; -*-
################################################################################
#
# sqlalchemy-pervasive -- SQLAlchemy Dialect for Pervasive PSQL
-# Copyright © 2013 Sacramento Natural Foods Co-op, Inc
+# Copyright © 2013-2024 Sacramento Natural Foods Co-op, Inc
#
# This file is part of sqlalchemy-pervasive.
#
@@ -22,16 +21,22 @@
#
################################################################################
-
+import os
import shutil
-from fabric.api import task, local
+
+from invoke import task
@task
-def release():
+def release(c):
"""
Release a new version of 'sqlalchemy-pervasive'.
"""
+ if os.path.exists('dist'):
+ shutil.rmtree('dist')
+ if os.path.exists('sqlalchemy_pervasive.egg-info'):
+ shutil.rmtree('sqlalchemy_pervasive.egg-info')
- shutil.rmtree('sqlalchemy_pervasive.egg-info')
- local('python setup.py sdist --formats=gztar register upload')
+ c.run('python -m build --sdist')
+
+ c.run('twine upload dist/*')