summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Pentchev <roam@ringlet.net>2019-01-12 23:01:07 +0200
committerPeter Pentchev <roam@ringlet.net>2019-01-12 23:01:07 +0200
commit7515de39b089395b94d54869466dcefd08742c18 (patch)
treefdb325a1a0a30fe85df09018b1f118e33dec79de
parent367327ed2e148878c968973fba209ce19b7f64cc (diff)
Add Python setuptools infrastructure.
-rw-r--r--python/README.md81
-rw-r--r--python/confget/__init__.py107
-rwxr-xr-xpython/confget/__main__.py12
-rw-r--r--python/confget/defs.py6
-rw-r--r--python/confget/py.typed0
-rw-r--r--python/setup.cfg2
-rw-r--r--python/setup.py132
-rw-r--r--python/tox.ini8
8 files changed, 335 insertions, 13 deletions
diff --git a/python/README.md b/python/README.md
new file mode 100644
index 0000000..890f138
--- /dev/null
+++ b/python/README.md
@@ -0,0 +1,81 @@
+confget - parse configuration files
+===================================
+
+The `confget` library parses configuration files (currently INI-style
+files and CGI `QUERY_STRING` environment variable) and allows a program
+to use the values defined in them. It provides various options for
+selecting the variable names and values to return and the configuration
+file sections to fetch them from.
+
+The `confget` library may also be used as a command-line tool with
+the same interface as the C implementation.
+
+The `confget` library is fully typed.
+
+Specifying configuration values for the backends
+------------------------------------------------
+
+The `confget.defs` module defines the `Config` class that is used to
+control the behavior of the various `confget` backends. Its main
+purpose is to specify the filename and, optionally, the section name for
+INI-style files, but other backends may use its fields in different ways.
+
+A `Config` object is created using the following parameters:
+- a list of variable names to query (may be empty)
+- `filename` (str, optional): the name of the file to open
+- `section` (str, default ""): the name of the section within the file
+- `section_specified` (bool, default false): if `section` is an empty
+ string, only fetch variables from the unnamed section at the start of
+ the file instead of defaulting to the first section in the file
+
+Parsing INI-style configuration files
+-------------------------------------
+
+The `confget` library's "ini" backend parses an INI-style configuration
+file. Its `read_file()` method parses the file and returns a dictionary
+of sections and the variables and their values within them:
+
+ import confget
+
+ cfg = confget.Config([], filename='config.ini')
+ ini = confget.BACKENDS['ini'](cfg)
+ data = ini.read_file()
+ print('Section names: {names}'.format(names=sorted(data.keys())))
+ print(data['server']['address'])
+
+Letting variables in a section override the default ones
+--------------------------------------------------------
+
+In some cases it is useful to have default values before the first
+named section in a file and then override some values in various
+sections. This may be useful for e.g. host-specific configuration
+kept in a section with the same name as the host.
+
+The `format` module in the `confget` library allows, among other
+filtering modes, to get the list of variables with a section
+overriding the default ones:
+
+ from confget import backend, format
+
+ cfg = format.FormatConfig(['foo'], filename='config.ini', section='first',
+ section_override=True)
+ ini = backend.BACKENDS['ini'](cfg)
+ data = ini.read_file()
+ res = format.filter_vars(cfg, data)
+ assert len(res) == 1, repr(res)
+ print(res[0].output_full)
+
+ cfg = format.FormatConfig(['foo'], filename='config.ini', section='second',
+ section_override=True)
+ ini = backend.BACKENDS['ini'](cfg)
+ data = ini.read_file()
+ res = format.filter_vars(cfg, data)
+ assert len(res) == 1, repr(res)
+ print(res[0].output_full)
+
+See the documentation of the `FormatConfig` class and the `filter_vars()`
+function in the `confget.format` module for more information and for
+a list of the various other filtering modes, all supported when
+the library is used as a command-line tool.
+
+Comments: Peter Pentchev <roam@ringlet.net>
diff --git a/python/confget/__init__.py b/python/confget/__init__.py
index e69de29..cbd8ee9 100644
--- a/python/confget/__init__.py
+++ b/python/confget/__init__.py
@@ -0,0 +1,107 @@
+# Copyright (c) 2019 Peter Pentchev <roam@ringlet.net>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+"""
+confget - a library for parsing configuration files
+
+The `confget` library parses configuration files (currently INI-style
+files and CGI `QUERY_STRING` environment variable) and allows a program
+to use the values defined in them. It provides various options for
+selecting the variable names and values to return and the configuration
+file sections to fetch them from.
+
+The `confget` library may also be used as a command-line tool with
+the same interface as the C implementation.
+
+The `confget` library is fully typed.
+
+Specifying configuration values for the backends
+------------------------------------------------
+
+The `confget.defs` module defines the `Config` class that is used to
+control the behavior of the various `confget` backends. Its main
+purpose is to specify the filename and, optionally, the section name for
+INI-style files, but other backends may use its fields in different ways.
+
+A `Config` object is created using the following parameters:
+- a list of variable names to query (may be empty)
+- `filename` (str, optional): the name of the file to open
+- `section` (str, default ""): the name of the section within the file
+- `section_specified` (bool, default false): if `section` is an empty
+ string, only fetch variables from the unnamed section at the start of
+ the file instead of defaulting to the first section in the file
+
+Parsing INI-style configuration files
+-------------------------------------
+
+The `confget` library's "ini" backend parses an INI-style configuration
+file. Its `read_file()` method parses the file and returns a dictionary
+of sections and the variables and their values within them:
+
+ import confget
+
+ cfg = confget.Config([], filename='config.ini')
+ ini = confget.BACKENDS['ini'](cfg)
+ data = ini.read_file()
+ print('Section names: {names}'.format(names=sorted(data.keys())))
+ print(data['server']['address'])
+
+Letting variables in a section override the default ones
+--------------------------------------------------------
+
+In some cases it is useful to have default values before the first
+named section in a file and then override some values in various
+sections. This may be useful for e.g. host-specific configuration
+kept in a section with the same name as the host.
+
+The `format` module in the `confget` library allows, among other
+filtering modes, to get the list of variables with a section
+overriding the default ones:
+
+ from confget import backend, format
+
+ cfg = format.FormatConfig(['foo'], filename='config.ini', section='first',
+ section_override=True)
+ ini = backend.BACKENDS['ini'](cfg)
+ data = ini.read_file()
+ res = format.filter_vars(cfg, data)
+ assert len(res) == 1, repr(res)
+ print(res[0].output_full)
+
+ cfg = format.FormatConfig(['foo'], filename='config.ini', section='second',
+ section_override=True)
+ ini = backend.BACKENDS['ini'](cfg)
+ data = ini.read_file()
+ res = format.filter_vars(cfg, data)
+ assert len(res) == 1, repr(res)
+ print(res[0].output_full)
+
+See the documentation of the `FormatConfig` class and the `filter_vars()`
+function in the `confget.format` module for more information and for
+a list of the various other filtering modes, all supported when
+the library is used as a command-line tool.
+"""
+
+from .defs import Config, VERSION_STRING # noqa: F401
+from .backend import BACKENDS # noqa: F401
diff --git a/python/confget/__main__.py b/python/confget/__main__.py
index 905877d..919fcae 100755
--- a/python/confget/__main__.py
+++ b/python/confget/__main__.py
@@ -44,12 +44,6 @@ except ImportError:
pass
-VERSION_STRING = '2.2.0'
-FEATURES = [
- ('BASE', VERSION_STRING),
-]
-
-
class MainConfig(fmt.FormatConfig):
# pylint: disable=too-few-public-methods,too-many-instance-attributes
"""
@@ -101,7 +95,7 @@ def version():
"""
Display program version information.
"""
- print('confget ' + VERSION_STRING)
+ print('confget ' + defs.VERSION_STRING)
def features(name):
@@ -112,10 +106,10 @@ def features(name):
if name is None:
print(' '.join([
'{name}={version}'.format(name=item[0], version=item[1])
- for item in FEATURES
+ for item in defs.FEATURES
]))
else:
- ver = dict(FEATURES).get(name, None)
+ ver = dict(defs.FEATURES).get(name, None)
if ver is None:
sys.exit(1)
print(ver)
diff --git a/python/confget/defs.py b/python/confget/defs.py
index 7f6a796..21ae7c5 100644
--- a/python/confget/defs.py
+++ b/python/confget/defs.py
@@ -36,6 +36,12 @@ except ImportError:
pass
+VERSION_STRING = '2.2.0'
+FEATURES = [
+ ('BASE', VERSION_STRING),
+]
+
+
class Config(object):
# pylint: disable=too-few-public-methods
""" Base class for the internal confget configuration. """
diff --git a/python/confget/py.typed b/python/confget/py.typed
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/python/confget/py.typed
diff --git a/python/setup.cfg b/python/setup.cfg
new file mode 100644
index 0000000..2a9acf1
--- /dev/null
+++ b/python/setup.cfg
@@ -0,0 +1,2 @@
+[bdist_wheel]
+universal = 1
diff --git a/python/setup.py b/python/setup.py
new file mode 100644
index 0000000..2d9f620
--- /dev/null
+++ b/python/setup.py
@@ -0,0 +1,132 @@
+#!/usr/bin/python3
+#
+# Copyright (c) 2019 Peter Pentchev <roam@ringlet.net>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+"""
+Setup infrastructure for confget, the configuration file parser.
+"""
+
+import re
+import setuptools # type: ignore
+
+
+RE_VERSION = r'''^
+ \s* VERSION_STRING \s* = \s* '
+ (?P<version>
+ (?: 0 | [1-9][0-9]* ) # major
+ \. (?: 0 | [1-9][0-9]* ) # minor
+ \. (?: 0 | [1-9][0-9]* ) # patchlevel
+ (?: \. [a-zA-Z0-9]+ )? # optional addendum (dev1, beta3, etc.)
+ )
+ ' \s*
+ $'''
+
+
+def get_version():
+ # type: () -> str
+ """ Get the version string from the module's __init__ file. """
+ found = None
+ re_semver = re.compile(RE_VERSION, re.X)
+ with open('confget/defs.py') as init:
+ for line in init.readlines():
+ match = re_semver.match(line)
+ if not match:
+ continue
+ assert found is None
+ found = match.group('version')
+
+ assert found is not None
+ return found
+
+
+def get_long_description():
+ # type: () -> str
+ """ Get the package long description from the README file. """
+ with open('README.md') as readme:
+ return readme.read()
+
+
+setuptools.setup(
+ name='confget',
+ version=get_version(),
+
+ description='Parse configuration files and extract values from them',
+ long_description=get_long_description(),
+ long_description_content_type='text/markdown',
+
+ author='Peter Pentchev',
+ author_email='roam@ringlet.net',
+ url='https://devel.ringlet.net/textproc/confget/',
+
+ packages=['confget', 'confget.backend'],
+ package_data={
+ 'confget': [
+ # The typed module marker
+ 'py.typed',
+ ],
+ },
+
+ install_requires=[
+ 'six',
+ ],
+
+ license='BSD-2',
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+
+ 'Environment :: Console',
+
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: System Administrators',
+
+ 'License :: DFSG approved',
+ 'License :: Freely Distributable',
+ 'License :: OSI Approved :: BSD License',
+
+ 'Operating System :: POSIX',
+ 'Operating System :: Unix',
+
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
+
+ 'Topic :: Software Development :: Libraries',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ 'Topic :: Utilities',
+ ],
+
+ entry_points={
+ 'console_scripts': [
+ 'confget=confget.__main__:main',
+ ],
+ },
+
+ zip_safe=True,
+)
diff --git a/python/tox.ini b/python/tox.ini
index 757d546..57f11d4 100644
--- a/python/tox.ini
+++ b/python/tox.ini
@@ -31,7 +31,7 @@ basepython = python3
deps =
flake8
commands =
- flake8 confget unit_tests ../t/defs/tools/generate.py
+ flake8 confget setup.py unit_tests ../t/defs/tools/generate.py
[testenv:mypy2]
basepython = python3
@@ -40,7 +40,7 @@ deps =
setenv =
MYPYPATH={toxinidir}/stubs
commands =
- mypy --strict --py2 confget
+ mypy --strict --py2 confget setup.py
mypy --strict --py2 --allow-untyped-decorators unit_tests
[testenv:mypy3]
@@ -50,7 +50,7 @@ deps =
setenv =
MYPYPATH={toxinidir}/stubs
commands =
- mypy --strict confget ../t/defs/tools/generate.py
+ mypy --strict confget setup.py ../t/defs/tools/generate.py
mypy --strict --allow-untyped-decorators unit_tests
[testenv:pylint]
@@ -60,7 +60,7 @@ deps =
pylint
pytest
commands =
- pylint --disable=useless-object-inheritance,duplicate-code confget unit_tests ../t/defs/tools/generate.py
+ pylint --disable=useless-object-inheritance,duplicate-code confget setup.py unit_tests ../t/defs/tools/generate.py
[testenv:unit_tests_2]
basepython = python2