Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
*.egg-info
__pycache__
*.pyc

uv.lock
.venv
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,6 @@ include ChangeLog
include INSTALL
include Makefile
include python-config.spec
include README
include README.rst
include tests/*.conf
include tests/*.py
2 changes: 1 addition & 1 deletion Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@ build:
$(PYTHON) setup.py build

check:
$(PYTHON) setup.py test
$(PYTHON) -m pytest tests

install:
$(PYTHON) setup.py install --skip-build $(INSTALL_FLAGS)
Expand Down
8 changes: 0 additions & 8 deletions README

This file was deleted.

88 changes: 88 additions & 0 deletions README.rst
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
A library for reading Python configuration files
================================================

About
-----

This library reads configuration files that are represented as Python modules
with restricted syntax. It uses the AST for parsing.
Values can be written back with ``dump`` and ``dumps``.

Two APIs are available:

* ``python_config.load(s)`` / ``dump(s)`` — read and write a **dict** (lowercase keys).

* ``python_config.document.load(s)`` / ``dump(s)`` — read and write a
:class:`~python_config.document.ConfigDocument`. Use this to change option
values from code and save the file back. Top-level options use uppercase names
(e.g. ``doc["LOG_LEVEL"] = 5``). Nested ``list`` and ``dict`` values are
plain Python objects; in-place mutations are reflected on dump.

Supported value types are:
- ``bool``,
- ``int``,
- ``float``,
- ``str``,
- ``list``,
- ``dict`` (with string keys).

Only **uppercase** names that do not start with ``_`` are exposed through the dict API.

Expressions in source files are **evaluated on load**:

* Arithmetic (e.g. ``COUNT = 1 + 2 * 3``)
* F-strings that reference earlier options (e.g. ``ENDPOINT = f"{HOST}:{PORT}"``)

``dump`` and ``dumps`` always emit **literal values** with aggressive formatting applied.
Hand-edited spacing and expression forms are not preserved on save.

Preamble comments, a module-level docstring, and per-assignment docstrings are
preserved through the document API.

.. NOTE::

If you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project or just use Pydantic.

Interactive viewer
------------------

A terminal UI is included for browsing large configuration files without scrolling
through thousands of lines in an editor. It loads configs through the document API
so f-strings, arithmetic, and comments are shown as in the source.

.. source:: bash

python-config-view /path/to/config.conf
python -m python_config.viewer /path/to/config.conf

Use ``--page-size`` to control pagination (default 20) and ``--no-color`` for
plain output.

Key bindings:

========== ======================================================
Key Action
========== ======================================================
``/`` Search variables or keys at the current level
``n``/``j`` Next item (or next page at end of page)
``p``/``k`` Previous item (or previous page at start of page)
``Enter`` Open a dict/list child or jump to a search result
``1``-``9`` Quick-select an item on the current page
``b`` Go back (or leave search results)
``s`` Expand the current value full-screen (scroll with ``j``/``n`` and ``k``/``p``)
``a`` Toggle assignment source view (top-level variables only)
``q`` Quit
========== ======================================================

Tests
-----

It's recommended to test against python3.6. Assuming here you have .venv36 for this.

.. source:: bash

poe test

# python 3.6
.venv36/bin/pytest -s -vvv tests/
84 changes: 84 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "python-config"
dynamic = ["version"]
description = "A library for reading Python configuration files"
authors = [
{ name = "Dmitry Konishchev", email = "konishchev@gmail.com" },
]
license = { text = "GPL-3.0" }
readme = "README.rst"

keywords = ["python-syntax", "configuration"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX",
"Operating System :: Unix",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython",
]

requires-python = ">=3.9"

dependencies = [
"click>=7,<9",
"rich>=10,<13",
]

[project.scripts]
python-config-view = "python_config.viewer.cli:main"

[project.optional-dependencies]
dev = [
"mypy>=1.19.1",
"poethepoet>=0.32",
"pytest>=8",
"ruff>=0.9",
]

[tool.setuptools.packages.find]
include = ["python_config", "python_config.*"]

[tool.setuptools.dynamic]
version = {file = "version.txt"}

[tool.poe.tasks.format]
help = "Format all the code."
shell = "ruff check --fix --select I python_config/ tests/ && ruff format python_config/ tests/"

[tool.poe.tasks.lint]
help = "Lint all the code."
cmd = "ruff check python_config/ tests/"

[tool.poe.tasks.typecheck]
help = "Typecheck all the code."
cmd = "mypy python_config"

[tool.poe.tasks.test]
help = "Run unit tests."
cmd = "pytest --strict-markers -vvv tests/"

[tool.poe.tasks.ci]
help = "Run full CI sequence."
sequence = [
"format",
"lint",
"typecheck",
"test",
]

[tool.ruff]
line-length = 100

[tool.ruff.lint.isort]
lines-after-imports = 2
26 changes: 20 additions & 6 deletions python-config.spec
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,6 @@

%bcond_without tests

%global project_name pcore
%global project_description %{expand:
Python configuration files themselves are actual Python files. The module
reads only values in uppercase from them, checks that they contain only basic
Expand All@@ -13,8 +12,8 @@ Note: if you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project.}

Name: python-config
Version: 0.1.2
Release: 4.ROCKIT3%{?dist}
Version: 1.0.0
Release: ROCKIT1%{?dist}
Summary: A simple module for reading Python configuration files

Group: Development/Libraries
Expand All@@ -34,7 +33,17 @@ BuildRequires: python%{python3_pkgversion}-devel
BuildRequires: python%{python3_pkgversion}-setuptools
%if 0%{with tests}
BuildRequires: python%{python3_pkgversion}-pytest >= 2.2.4
BuildRequires: python%{python3_pkgversion}-click
BuildRequires: python%{python3_pkgversion}-rich
%if 0%{?rhel} <= 8
BuildRequires: python3-dataclasses
%endif
%endif # with tests
%if 0%{?rhel} <= 8
Requires: python3-dataclasses
%endif
Requires: python%{python3_pkgversion}-click
Requires: python%{python3_pkgversion}-rich
Obsoletes: python36-config
Conflicts: python36-config

Expand All@@ -59,17 +68,22 @@ make PYTHON=%{__python3} check

%files -n python%{python3_pkgversion}-config
%defattr(-,root,root,-)
%{python3_sitelib}/python_config.py
%{python3_sitelib}/__pycache__/python_config.*.py*
%{_bindir}/python-config-view
%{python3_sitelib}/python_config/
%{python3_sitelib}/python_config/__pycache__/
%{python3_sitelib}/python_config-%{version}-*.egg-info
%doc ChangeLog INSTALL README
%doc ChangeLog INSTALL README.rst


%clean
[ "%buildroot" = "/" ] || rm -rf "%buildroot"


%changelog
* Tue May 19 2026 Pavel Kulyov <kulyov.pavel@gmail.com> - 1.0.0-1
- Version 1.0.0: AST-based config parsing (no exec), add loads/dump/dumps
- Restructure the project splitting single-module, update metadata

* Tue Jan 24 2023 Andrey Kulaev <adkulaev@gmail.com> - 0.1.2-4
- Add centos 8.4 support

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
*.egg-info
__pycache__
*.pyc

uv.lock
.venv
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,6 @@ include ChangeLog
include INSTALL
include Makefile
include python-config.spec
include README
include README.rst
include tests/*.conf
include tests/*.py
2 changes: 1 addition & 1 deletion Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@ build:
$(PYTHON) setup.py build

check:
$(PYTHON) setup.py test
$(PYTHON) -m pytest tests

install:
$(PYTHON) setup.py install --skip-build $(INSTALL_FLAGS)
Expand Down
8 changes: 0 additions & 8 deletions README

This file was deleted.

88 changes: 88 additions & 0 deletions README.rst
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
A library for reading Python configuration files
================================================

About
-----

This library reads configuration files that are represented as Python modules
with restricted syntax. It uses the AST for parsing.
Values can be written back with ``dump`` and ``dumps``.

Two APIs are available:

* ``python_config.load(s)`` / ``dump(s)`` — read and write a **dict** (lowercase keys).

* ``python_config.document.load(s)`` / ``dump(s)`` — read and write a
:class:`~python_config.document.ConfigDocument`. Use this to change option
values from code and save the file back. Top-level options use uppercase names
(e.g. ``doc["LOG_LEVEL"] = 5``). Nested ``list`` and ``dict`` values are
plain Python objects; in-place mutations are reflected on dump.

Supported value types are:
- ``bool``,
- ``int``,
- ``float``,
- ``str``,
- ``list``,
- ``dict`` (with string keys).

Only **uppercase** names that do not start with ``_`` are exposed through the dict API.

Expressions in source files are **evaluated on load**:

* Arithmetic (e.g. ``COUNT = 1 + 2 * 3``)
* F-strings that reference earlier options (e.g. ``ENDPOINT = f"{HOST}:{PORT}"``)

``dump`` and ``dumps`` always emit **literal values** with aggressive formatting applied.
Hand-edited spacing and expression forms are not preserved on save.

Preamble comments, a module-level docstring, and per-assignment docstrings are
preserved through the document API.

.. NOTE::

If you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project or just use Pydantic.

Interactive viewer
------------------

A terminal UI is included for browsing large configuration files without scrolling
through thousands of lines in an editor. It loads configs through the document API
so f-strings, arithmetic, and comments are shown as in the source.

.. source:: bash

python-config-view /path/to/config.conf
python -m python_config.viewer /path/to/config.conf

Use ``--page-size`` to control pagination (default 20) and ``--no-color`` for
plain output.

Key bindings:

========== ======================================================
Key Action
========== ======================================================
``/`` Search variables or keys at the current level
``n``/``j`` Next item (or next page at end of page)
``p``/``k`` Previous item (or previous page at start of page)
``Enter`` Open a dict/list child or jump to a search result
``1``-``9`` Quick-select an item on the current page
``b`` Go back (or leave search results)
``s`` Expand the current value full-screen (scroll with ``j``/``n`` and ``k``/``p``)
``a`` Toggle assignment source view (top-level variables only)
``q`` Quit
========== ======================================================

Tests
-----

It's recommended to test against python3.6. Assuming here you have .venv36 for this.

.. source:: bash

poe test

# python 3.6
.venv36/bin/pytest -s -vvv tests/
84 changes: 84 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "python-config"
dynamic = ["version"]
description = "A library for reading Python configuration files"
authors = [
{ name = "Dmitry Konishchev", email = "konishchev@gmail.com" },
]
license = { text = "GPL-3.0" }
readme = "README.rst"

keywords = ["python-syntax", "configuration"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX",
"Operating System :: Unix",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython",
]

requires-python = ">=3.9"

dependencies = [
"click>=7,<9",
"rich>=10,<13",
]

[project.scripts]
python-config-view = "python_config.viewer.cli:main"

[project.optional-dependencies]
dev = [
"mypy>=1.19.1",
"poethepoet>=0.32",
"pytest>=8",
"ruff>=0.9",
]

[tool.setuptools.packages.find]
include = ["python_config", "python_config.*"]

[tool.setuptools.dynamic]
version = {file = "version.txt"}

[tool.poe.tasks.format]
help = "Format all the code."
shell = "ruff check --fix --select I python_config/ tests/ && ruff format python_config/ tests/"

[tool.poe.tasks.lint]
help = "Lint all the code."
cmd = "ruff check python_config/ tests/"

[tool.poe.tasks.typecheck]
help = "Typecheck all the code."
cmd = "mypy python_config"

[tool.poe.tasks.test]
help = "Run unit tests."
cmd = "pytest --strict-markers -vvv tests/"

[tool.poe.tasks.ci]
help = "Run full CI sequence."
sequence = [
"format",
"lint",
"typecheck",
"test",
]

[tool.ruff]
line-length = 100

[tool.ruff.lint.isort]
lines-after-imports = 2
26 changes: 20 additions & 6 deletions python-config.spec
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,6 @@

%bcond_without tests

%global project_name pcore
%global project_description %{expand:
Python configuration files themselves are actual Python files. The module
reads only values in uppercase from them, checks that they contain only basic
Expand All@@ -13,8 +12,8 @@ Note: if you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project.}

Name: python-config
Version: 0.1.2
Release: 4.ROCKIT3%{?dist}
Version: 1.0.0
Release: ROCKIT1%{?dist}
Summary: A simple module for reading Python configuration files

Group: Development/Libraries
Expand All@@ -34,7 +33,17 @@ BuildRequires: python%{python3_pkgversion}-devel
BuildRequires: python%{python3_pkgversion}-setuptools
%if 0%{with tests}
BuildRequires: python%{python3_pkgversion}-pytest >= 2.2.4
BuildRequires: python%{python3_pkgversion}-click
BuildRequires: python%{python3_pkgversion}-rich
%if 0%{?rhel} <= 8
BuildRequires: python3-dataclasses
%endif
%endif # with tests
%if 0%{?rhel} <= 8
Requires: python3-dataclasses
%endif
Requires: python%{python3_pkgversion}-click
Requires: python%{python3_pkgversion}-rich
Obsoletes: python36-config
Conflicts: python36-config

Expand All@@ -59,17 +68,22 @@ make PYTHON=%{__python3} check

%files -n python%{python3_pkgversion}-config
%defattr(-,root,root,-)
%{python3_sitelib}/python_config.py
%{python3_sitelib}/__pycache__/python_config.*.py*
%{_bindir}/python-config-view
%{python3_sitelib}/python_config/
%{python3_sitelib}/python_config/__pycache__/
%{python3_sitelib}/python_config-%{version}-*.egg-info
%doc ChangeLog INSTALL README
%doc ChangeLog INSTALL README.rst


%clean
[ "%buildroot" = "/" ] || rm -rf "%buildroot"


%changelog
* Tue May 19 2026 Pavel Kulyov <kulyov.pavel@gmail.com> - 1.0.0-1
- Version 1.0.0: AST-based config parsing (no exec), add loads/dump/dumps
- Restructure the project splitting single-module, update metadata

* Tue Jan 24 2023 Andrey Kulaev <adkulaev@gmail.com> - 0.1.2-4
- Add centos 8.4 support

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
*.egg-info
__pycache__
*.pyc

uv.lock
.venv
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,6 @@ include ChangeLog
include INSTALL
include Makefile
include python-config.spec
include README
include README.rst
include tests/*.conf
include tests/*.py
2 changes: 1 addition & 1 deletion Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@ build:
$(PYTHON) setup.py build

check:
$(PYTHON) setup.py test
$(PYTHON) -m pytest tests

install:
$(PYTHON) setup.py install --skip-build $(INSTALL_FLAGS)
Expand Down
8 changes: 0 additions & 8 deletions README

This file was deleted.

88 changes: 88 additions & 0 deletions README.rst
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
A library for reading Python configuration files
================================================

About
-----

This library reads configuration files that are represented as Python modules
with restricted syntax. It uses the AST for parsing.
Values can be written back with ``dump`` and ``dumps``.

Two APIs are available:

* ``python_config.load(s)`` / ``dump(s)`` — read and write a **dict** (lowercase keys).

* ``python_config.document.load(s)`` / ``dump(s)`` — read and write a
:class:`~python_config.document.ConfigDocument`. Use this to change option
values from code and save the file back. Top-level options use uppercase names
(e.g. ``doc["LOG_LEVEL"] = 5``). Nested ``list`` and ``dict`` values are
plain Python objects; in-place mutations are reflected on dump.

Supported value types are:
- ``bool``,
- ``int``,
- ``float``,
- ``str``,
- ``list``,
- ``dict`` (with string keys).

Only **uppercase** names that do not start with ``_`` are exposed through the dict API.

Expressions in source files are **evaluated on load**:

* Arithmetic (e.g. ``COUNT = 1 + 2 * 3``)
* F-strings that reference earlier options (e.g. ``ENDPOINT = f"{HOST}:{PORT}"``)

``dump`` and ``dumps`` always emit **literal values** with aggressive formatting applied.
Hand-edited spacing and expression forms are not preserved on save.

Preamble comments, a module-level docstring, and per-assignment docstrings are
preserved through the document API.

.. NOTE::

If you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project or just use Pydantic.

Interactive viewer
------------------

A terminal UI is included for browsing large configuration files without scrolling
through thousands of lines in an editor. It loads configs through the document API
so f-strings, arithmetic, and comments are shown as in the source.

.. source:: bash

python-config-view /path/to/config.conf
python -m python_config.viewer /path/to/config.conf

Use ``--page-size`` to control pagination (default 20) and ``--no-color`` for
plain output.

Key bindings:

========== ======================================================
Key Action
========== ======================================================
``/`` Search variables or keys at the current level
``n``/``j`` Next item (or next page at end of page)
``p``/``k`` Previous item (or previous page at start of page)
``Enter`` Open a dict/list child or jump to a search result
``1``-``9`` Quick-select an item on the current page
``b`` Go back (or leave search results)
``s`` Expand the current value full-screen (scroll with ``j``/``n`` and ``k``/``p``)
``a`` Toggle assignment source view (top-level variables only)
``q`` Quit
========== ======================================================

Tests
-----

It's recommended to test against python3.6. Assuming here you have .venv36 for this.

.. source:: bash

poe test

# python 3.6
.venv36/bin/pytest -s -vvv tests/
84 changes: 84 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "python-config"
dynamic = ["version"]
description = "A library for reading Python configuration files"
authors = [
{ name = "Dmitry Konishchev", email = "konishchev@gmail.com" },
]
license = { text = "GPL-3.0" }
readme = "README.rst"

keywords = ["python-syntax", "configuration"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX",
"Operating System :: Unix",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython",
]

requires-python = ">=3.9"

dependencies = [
"click>=7,<9",
"rich>=10,<13",
]

[project.scripts]
python-config-view = "python_config.viewer.cli:main"

[project.optional-dependencies]
dev = [
"mypy>=1.19.1",
"poethepoet>=0.32",
"pytest>=8",
"ruff>=0.9",
]

[tool.setuptools.packages.find]
include = ["python_config", "python_config.*"]

[tool.setuptools.dynamic]
version = {file = "version.txt"}

[tool.poe.tasks.format]
help = "Format all the code."
shell = "ruff check --fix --select I python_config/ tests/ && ruff format python_config/ tests/"

[tool.poe.tasks.lint]
help = "Lint all the code."
cmd = "ruff check python_config/ tests/"

[tool.poe.tasks.typecheck]
help = "Typecheck all the code."
cmd = "mypy python_config"

[tool.poe.tasks.test]
help = "Run unit tests."
cmd = "pytest --strict-markers -vvv tests/"

[tool.poe.tasks.ci]
help = "Run full CI sequence."
sequence = [
"format",
"lint",
"typecheck",
"test",
]

[tool.ruff]
line-length = 100

[tool.ruff.lint.isort]
lines-after-imports = 2
26 changes: 20 additions & 6 deletions python-config.spec
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,6 @@

%bcond_without tests

%global project_name pcore
%global project_description %{expand:
Python configuration files themselves are actual Python files. The module
reads only values in uppercase from them, checks that they contain only basic
Expand All@@ -13,8 +12,8 @@ Note: if you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project.}

Name: python-config
Version: 0.1.2
Release: 4.ROCKIT3%{?dist}
Version: 1.0.0
Release: ROCKIT1%{?dist}
Summary: A simple module for reading Python configuration files

Group: Development/Libraries
Expand All@@ -34,7 +33,17 @@ BuildRequires: python%{python3_pkgversion}-devel
BuildRequires: python%{python3_pkgversion}-setuptools
%if 0%{with tests}
BuildRequires: python%{python3_pkgversion}-pytest >= 2.2.4
BuildRequires: python%{python3_pkgversion}-click
BuildRequires: python%{python3_pkgversion}-rich
%if 0%{?rhel} <= 8
BuildRequires: python3-dataclasses
%endif
%endif # with tests
%if 0%{?rhel} <= 8
Requires: python3-dataclasses
%endif
Requires: python%{python3_pkgversion}-click
Requires: python%{python3_pkgversion}-rich
Obsoletes: python36-config
Conflicts: python36-config

Expand All@@ -59,17 +68,22 @@ make PYTHON=%{__python3} check

%files -n python%{python3_pkgversion}-config
%defattr(-,root,root,-)
%{python3_sitelib}/python_config.py
%{python3_sitelib}/__pycache__/python_config.*.py*
%{_bindir}/python-config-view
%{python3_sitelib}/python_config/
%{python3_sitelib}/python_config/__pycache__/
%{python3_sitelib}/python_config-%{version}-*.egg-info
%doc ChangeLog INSTALL README
%doc ChangeLog INSTALL README.rst


%clean
[ "%buildroot" = "/" ] || rm -rf "%buildroot"


%changelog
* Tue May 19 2026 Pavel Kulyov <kulyov.pavel@gmail.com> - 1.0.0-1
- Version 1.0.0: AST-based config parsing (no exec), add loads/dump/dumps
- Restructure the project splitting single-module, update metadata

* Tue Jan 24 2023 Andrey Kulaev <adkulaev@gmail.com> - 0.1.2-4
- Add centos 8.4 support

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
*.egg-info
__pycache__
*.pyc

uv.lock
.venv
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,6 @@ include ChangeLog
include INSTALL
include Makefile
include python-config.spec
include README
include README.rst
include tests/*.conf
include tests/*.py
2 changes: 1 addition & 1 deletion Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@ build:
$(PYTHON) setup.py build

check:
$(PYTHON) setup.py test
$(PYTHON) -m pytest tests

install:
$(PYTHON) setup.py install --skip-build $(INSTALL_FLAGS)
Expand Down
8 changes: 0 additions & 8 deletions README

This file was deleted.

88 changes: 88 additions & 0 deletions README.rst
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
A library for reading Python configuration files
================================================

About
-----

This library reads configuration files that are represented as Python modules
with restricted syntax. It uses the AST for parsing.
Values can be written back with ``dump`` and ``dumps``.

Two APIs are available:

* ``python_config.load(s)`` / ``dump(s)`` — read and write a **dict** (lowercase keys).

* ``python_config.document.load(s)`` / ``dump(s)`` — read and write a
:class:`~python_config.document.ConfigDocument`. Use this to change option
values from code and save the file back. Top-level options use uppercase names
(e.g. ``doc["LOG_LEVEL"] = 5``). Nested ``list`` and ``dict`` values are
plain Python objects; in-place mutations are reflected on dump.

Supported value types are:
- ``bool``,
- ``int``,
- ``float``,
- ``str``,
- ``list``,
- ``dict`` (with string keys).

Only **uppercase** names that do not start with ``_`` are exposed through the dict API.

Expressions in source files are **evaluated on load**:

* Arithmetic (e.g. ``COUNT = 1 + 2 * 3``)
* F-strings that reference earlier options (e.g. ``ENDPOINT = f"{HOST}:{PORT}"``)

``dump`` and ``dumps`` always emit **literal values** with aggressive formatting applied.
Hand-edited spacing and expression forms are not preserved on save.

Preamble comments, a module-level docstring, and per-assignment docstrings are
preserved through the document API.

.. NOTE::

If you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project or just use Pydantic.

Interactive viewer
------------------

A terminal UI is included for browsing large configuration files without scrolling
through thousands of lines in an editor. It loads configs through the document API
so f-strings, arithmetic, and comments are shown as in the source.

.. source:: bash

python-config-view /path/to/config.conf
python -m python_config.viewer /path/to/config.conf

Use ``--page-size`` to control pagination (default 20) and ``--no-color`` for
plain output.

Key bindings:

========== ======================================================
Key Action
========== ======================================================
``/`` Search variables or keys at the current level
``n``/``j`` Next item (or next page at end of page)
``p``/``k`` Previous item (or previous page at start of page)
``Enter`` Open a dict/list child or jump to a search result
``1``-``9`` Quick-select an item on the current page
``b`` Go back (or leave search results)
``s`` Expand the current value full-screen (scroll with ``j``/``n`` and ``k``/``p``)
``a`` Toggle assignment source view (top-level variables only)
``q`` Quit
========== ======================================================

Tests
-----

It's recommended to test against python3.6. Assuming here you have .venv36 for this.

.. source:: bash

poe test

# python 3.6
.venv36/bin/pytest -s -vvv tests/
84 changes: 84 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "python-config"
dynamic = ["version"]
description = "A library for reading Python configuration files"
authors = [
{ name = "Dmitry Konishchev", email = "konishchev@gmail.com" },
]
license = { text = "GPL-3.0" }
readme = "README.rst"

keywords = ["python-syntax", "configuration"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX",
"Operating System :: Unix",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython",
]

requires-python = ">=3.9"

dependencies = [
"click>=7,<9",
"rich>=10,<13",
]

[project.scripts]
python-config-view = "python_config.viewer.cli:main"

[project.optional-dependencies]
dev = [
"mypy>=1.19.1",
"poethepoet>=0.32",
"pytest>=8",
"ruff>=0.9",
]

[tool.setuptools.packages.find]
include = ["python_config", "python_config.*"]

[tool.setuptools.dynamic]
version = {file = "version.txt"}

[tool.poe.tasks.format]
help = "Format all the code."
shell = "ruff check --fix --select I python_config/ tests/ && ruff format python_config/ tests/"

[tool.poe.tasks.lint]
help = "Lint all the code."
cmd = "ruff check python_config/ tests/"

[tool.poe.tasks.typecheck]
help = "Typecheck all the code."
cmd = "mypy python_config"

[tool.poe.tasks.test]
help = "Run unit tests."
cmd = "pytest --strict-markers -vvv tests/"

[tool.poe.tasks.ci]
help = "Run full CI sequence."
sequence = [
"format",
"lint",
"typecheck",
"test",
]

[tool.ruff]
line-length = 100

[tool.ruff.lint.isort]
lines-after-imports = 2
26 changes: 20 additions & 6 deletions python-config.spec
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,6 @@

%bcond_without tests

%global project_name pcore
%global project_description %{expand:
Python configuration files themselves are actual Python files. The module
reads only values in uppercase from them, checks that they contain only basic
Expand All@@ -13,8 +12,8 @@ Note: if you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project.}

Name: python-config
Version: 0.1.2
Release: 4.ROCKIT3%{?dist}
Version: 1.0.0
Release: ROCKIT1%{?dist}
Summary: A simple module for reading Python configuration files

Group: Development/Libraries
Expand All@@ -34,7 +33,17 @@ BuildRequires: python%{python3_pkgversion}-devel
BuildRequires: python%{python3_pkgversion}-setuptools
%if 0%{with tests}
BuildRequires: python%{python3_pkgversion}-pytest >= 2.2.4
BuildRequires: python%{python3_pkgversion}-click
BuildRequires: python%{python3_pkgversion}-rich
%if 0%{?rhel} <= 8
BuildRequires: python3-dataclasses
%endif
%endif # with tests
%if 0%{?rhel} <= 8
Requires: python3-dataclasses
%endif
Requires: python%{python3_pkgversion}-click
Requires: python%{python3_pkgversion}-rich
Obsoletes: python36-config
Conflicts: python36-config

Expand All@@ -59,17 +68,22 @@ make PYTHON=%{__python3} check

%files -n python%{python3_pkgversion}-config
%defattr(-,root,root,-)
%{python3_sitelib}/python_config.py
%{python3_sitelib}/__pycache__/python_config.*.py*
%{_bindir}/python-config-view
%{python3_sitelib}/python_config/
%{python3_sitelib}/python_config/__pycache__/
%{python3_sitelib}/python_config-%{version}-*.egg-info
%doc ChangeLog INSTALL README
%doc ChangeLog INSTALL README.rst


%clean
[ "%buildroot" = "/" ] || rm -rf "%buildroot"


%changelog
* Tue May 19 2026 Pavel Kulyov <kulyov.pavel@gmail.com> - 1.0.0-1
- Version 1.0.0: AST-based config parsing (no exec), add loads/dump/dumps
- Restructure the project splitting single-module, update metadata

* Tue Jan 24 2023 Andrey Kulaev <adkulaev@gmail.com> - 0.1.2-4
- Add centos 8.4 support

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
*.egg-info
__pycache__
*.pyc

uv.lock
.venv
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,6 @@ include ChangeLog
include INSTALL
include Makefile
include python-config.spec
include README
include README.rst
include tests/*.conf
include tests/*.py
2 changes: 1 addition & 1 deletion Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@ build:
$(PYTHON) setup.py build

check:
$(PYTHON) setup.py test
$(PYTHON) -m pytest tests

install:
$(PYTHON) setup.py install --skip-build $(INSTALL_FLAGS)
Expand Down
8 changes: 0 additions & 8 deletions README

This file was deleted.

88 changes: 88 additions & 0 deletions README.rst
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
A library for reading Python configuration files
================================================

About
-----

This library reads configuration files that are represented as Python modules
with restricted syntax. It uses the AST for parsing.
Values can be written back with ``dump`` and ``dumps``.

Two APIs are available:

* ``python_config.load(s)`` / ``dump(s)`` — read and write a **dict** (lowercase keys).

* ``python_config.document.load(s)`` / ``dump(s)`` — read and write a
:class:`~python_config.document.ConfigDocument`. Use this to change option
values from code and save the file back. Top-level options use uppercase names
(e.g. ``doc["LOG_LEVEL"] = 5``). Nested ``list`` and ``dict`` values are
plain Python objects; in-place mutations are reflected on dump.

Supported value types are:
- ``bool``,
- ``int``,
- ``float``,
- ``str``,
- ``list``,
- ``dict`` (with string keys).

Only **uppercase** names that do not start with ``_`` are exposed through the dict API.

Expressions in source files are **evaluated on load**:

* Arithmetic (e.g. ``COUNT = 1 + 2 * 3``)
* F-strings that reference earlier options (e.g. ``ENDPOINT = f"{HOST}:{PORT}"``)

``dump`` and ``dumps`` always emit **literal values** with aggressive formatting applied.
Hand-edited spacing and expression forms are not preserved on save.

Preamble comments, a module-level docstring, and per-assignment docstrings are
preserved through the document API.

.. NOTE::

If you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project or just use Pydantic.

Interactive viewer
------------------

A terminal UI is included for browsing large configuration files without scrolling
through thousands of lines in an editor. It loads configs through the document API
so f-strings, arithmetic, and comments are shown as in the source.

.. source:: bash

python-config-view /path/to/config.conf
python -m python_config.viewer /path/to/config.conf

Use ``--page-size`` to control pagination (default 20) and ``--no-color`` for
plain output.

Key bindings:

========== ======================================================
Key Action
========== ======================================================
``/`` Search variables or keys at the current level
``n``/``j`` Next item (or next page at end of page)
``p``/``k`` Previous item (or previous page at start of page)
``Enter`` Open a dict/list child or jump to a search result
``1``-``9`` Quick-select an item on the current page
``b`` Go back (or leave search results)
``s`` Expand the current value full-screen (scroll with ``j``/``n`` and ``k``/``p``)
``a`` Toggle assignment source view (top-level variables only)
``q`` Quit
========== ======================================================

Tests
-----

It's recommended to test against python3.6. Assuming here you have .venv36 for this.

.. source:: bash

poe test

# python 3.6
.venv36/bin/pytest -s -vvv tests/
84 changes: 84 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "python-config"
dynamic = ["version"]
description = "A library for reading Python configuration files"
authors = [
{ name = "Dmitry Konishchev", email = "konishchev@gmail.com" },
]
license = { text = "GPL-3.0" }
readme = "README.rst"

keywords = ["python-syntax", "configuration"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX",
"Operating System :: Unix",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython",
]

requires-python = ">=3.9"

dependencies = [
"click>=7,<9",
"rich>=10,<13",
]

[project.scripts]
python-config-view = "python_config.viewer.cli:main"

[project.optional-dependencies]
dev = [
"mypy>=1.19.1",
"poethepoet>=0.32",
"pytest>=8",
"ruff>=0.9",
]

[tool.setuptools.packages.find]
include = ["python_config", "python_config.*"]

[tool.setuptools.dynamic]
version = {file = "version.txt"}

[tool.poe.tasks.format]
help = "Format all the code."
shell = "ruff check --fix --select I python_config/ tests/ && ruff format python_config/ tests/"

[tool.poe.tasks.lint]
help = "Lint all the code."
cmd = "ruff check python_config/ tests/"

[tool.poe.tasks.typecheck]
help = "Typecheck all the code."
cmd = "mypy python_config"

[tool.poe.tasks.test]
help = "Run unit tests."
cmd = "pytest --strict-markers -vvv tests/"

[tool.poe.tasks.ci]
help = "Run full CI sequence."
sequence = [
"format",
"lint",
"typecheck",
"test",
]

[tool.ruff]
line-length = 100

[tool.ruff.lint.isort]
lines-after-imports = 2
26 changes: 20 additions & 6 deletions python-config.spec
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,6 @@

%bcond_without tests

%global project_name pcore
%global project_description %{expand:
Python configuration files themselves are actual Python files. The module
reads only values in uppercase from them, checks that they contain only basic
Expand All@@ -13,8 +12,8 @@ Note: if you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project.}

Name: python-config
Version: 0.1.2
Release: 4.ROCKIT3%{?dist}
Version: 1.0.0
Release: ROCKIT1%{?dist}
Summary: A simple module for reading Python configuration files

Group: Development/Libraries
Expand All@@ -34,7 +33,17 @@ BuildRequires: python%{python3_pkgversion}-devel
BuildRequires: python%{python3_pkgversion}-setuptools
%if 0%{with tests}
BuildRequires: python%{python3_pkgversion}-pytest >= 2.2.4
BuildRequires: python%{python3_pkgversion}-click
BuildRequires: python%{python3_pkgversion}-rich
%if 0%{?rhel} <= 8
BuildRequires: python3-dataclasses
%endif
%endif # with tests
%if 0%{?rhel} <= 8
Requires: python3-dataclasses
%endif
Requires: python%{python3_pkgversion}-click
Requires: python%{python3_pkgversion}-rich
Obsoletes: python36-config
Conflicts: python36-config

Expand All@@ -59,17 +68,22 @@ make PYTHON=%{__python3} check

%files -n python%{python3_pkgversion}-config
%defattr(-,root,root,-)
%{python3_sitelib}/python_config.py
%{python3_sitelib}/__pycache__/python_config.*.py*
%{_bindir}/python-config-view
%{python3_sitelib}/python_config/
%{python3_sitelib}/python_config/__pycache__/
%{python3_sitelib}/python_config-%{version}-*.egg-info
%doc ChangeLog INSTALL README
%doc ChangeLog INSTALL README.rst


%clean
[ "%buildroot" = "/" ] || rm -rf "%buildroot"


%changelog
* Tue May 19 2026 Pavel Kulyov <kulyov.pavel@gmail.com> - 1.0.0-1
- Version 1.0.0: AST-based config parsing (no exec), add loads/dump/dumps
- Restructure the project splitting single-module, update metadata

* Tue Jan 24 2023 Andrey Kulaev <adkulaev@gmail.com> - 0.1.2-4
- Add centos 8.4 support

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
*.egg-info
__pycache__
*.pyc

uv.lock
.venv
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,6 @@ include ChangeLog
include INSTALL
include Makefile
include python-config.spec
include README
include README.rst
include tests/*.conf
include tests/*.py
2 changes: 1 addition & 1 deletion Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@ build:
$(PYTHON) setup.py build

check:
$(PYTHON) setup.py test
$(PYTHON) -m pytest tests

install:
$(PYTHON) setup.py install --skip-build $(INSTALL_FLAGS)
Expand Down
8 changes: 0 additions & 8 deletions README

This file was deleted.

88 changes: 88 additions & 0 deletions README.rst
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
A library for reading Python configuration files
================================================

About
-----

This library reads configuration files that are represented as Python modules
with restricted syntax. It uses the AST for parsing.
Values can be written back with ``dump`` and ``dumps``.

Two APIs are available:

* ``python_config.load(s)`` / ``dump(s)`` — read and write a **dict** (lowercase keys).

* ``python_config.document.load(s)`` / ``dump(s)`` — read and write a
:class:`~python_config.document.ConfigDocument`. Use this to change option
values from code and save the file back. Top-level options use uppercase names
(e.g. ``doc["LOG_LEVEL"] = 5``). Nested ``list`` and ``dict`` values are
plain Python objects; in-place mutations are reflected on dump.

Supported value types are:
- ``bool``,
- ``int``,
- ``float``,
- ``str``,
- ``list``,
- ``dict`` (with string keys).

Only **uppercase** names that do not start with ``_`` are exposed through the dict API.

Expressions in source files are **evaluated on load**:

* Arithmetic (e.g. ``COUNT = 1 + 2 * 3``)
* F-strings that reference earlier options (e.g. ``ENDPOINT = f"{HOST}:{PORT}"``)

``dump`` and ``dumps`` always emit **literal values** with aggressive formatting applied.
Hand-edited spacing and expression forms are not preserved on save.

Preamble comments, a module-level docstring, and per-assignment docstrings are
preserved through the document API.

.. NOTE::

If you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project or just use Pydantic.

Interactive viewer
------------------

A terminal UI is included for browsing large configuration files without scrolling
through thousands of lines in an editor. It loads configs through the document API
so f-strings, arithmetic, and comments are shown as in the source.

.. source:: bash

python-config-view /path/to/config.conf
python -m python_config.viewer /path/to/config.conf

Use ``--page-size`` to control pagination (default 20) and ``--no-color`` for
plain output.

Key bindings:

========== ======================================================
Key Action
========== ======================================================
``/`` Search variables or keys at the current level
``n``/``j`` Next item (or next page at end of page)
``p``/``k`` Previous item (or previous page at start of page)
``Enter`` Open a dict/list child or jump to a search result
``1``-``9`` Quick-select an item on the current page
``b`` Go back (or leave search results)
``s`` Expand the current value full-screen (scroll with ``j``/``n`` and ``k``/``p``)
``a`` Toggle assignment source view (top-level variables only)
``q`` Quit
========== ======================================================

Tests
-----

It's recommended to test against python3.6. Assuming here you have .venv36 for this.

.. source:: bash

poe test

# python 3.6
.venv36/bin/pytest -s -vvv tests/
84 changes: 84 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "python-config"
dynamic = ["version"]
description = "A library for reading Python configuration files"
authors = [
{ name = "Dmitry Konishchev", email = "konishchev@gmail.com" },
]
license = { text = "GPL-3.0" }
readme = "README.rst"

keywords = ["python-syntax", "configuration"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX",
"Operating System :: Unix",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython",
]

requires-python = ">=3.9"

dependencies = [
"click>=7,<9",
"rich>=10,<13",
]

[project.scripts]
python-config-view = "python_config.viewer.cli:main"

[project.optional-dependencies]
dev = [
"mypy>=1.19.1",
"poethepoet>=0.32",
"pytest>=8",
"ruff>=0.9",
]

[tool.setuptools.packages.find]
include = ["python_config", "python_config.*"]

[tool.setuptools.dynamic]
version = {file = "version.txt"}

[tool.poe.tasks.format]
help = "Format all the code."
shell = "ruff check --fix --select I python_config/ tests/ && ruff format python_config/ tests/"

[tool.poe.tasks.lint]
help = "Lint all the code."
cmd = "ruff check python_config/ tests/"

[tool.poe.tasks.typecheck]
help = "Typecheck all the code."
cmd = "mypy python_config"

[tool.poe.tasks.test]
help = "Run unit tests."
cmd = "pytest --strict-markers -vvv tests/"

[tool.poe.tasks.ci]
help = "Run full CI sequence."
sequence = [
"format",
"lint",
"typecheck",
"test",
]

[tool.ruff]
line-length = 100

[tool.ruff.lint.isort]
lines-after-imports = 2
26 changes: 20 additions & 6 deletions python-config.spec
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,6 @@

%bcond_without tests

%global project_name pcore
%global project_description %{expand:
Python configuration files themselves are actual Python files. The module
reads only values in uppercase from them, checks that they contain only basic
Expand All@@ -13,8 +12,8 @@ Note: if you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project.}

Name: python-config
Version: 0.1.2
Release: 4.ROCKIT3%{?dist}
Version: 1.0.0
Release: ROCKIT1%{?dist}
Summary: A simple module for reading Python configuration files

Group: Development/Libraries
Expand All@@ -34,7 +33,17 @@ BuildRequires: python%{python3_pkgversion}-devel
BuildRequires: python%{python3_pkgversion}-setuptools
%if 0%{with tests}
BuildRequires: python%{python3_pkgversion}-pytest >= 2.2.4
BuildRequires: python%{python3_pkgversion}-click
BuildRequires: python%{python3_pkgversion}-rich
%if 0%{?rhel} <= 8
BuildRequires: python3-dataclasses
%endif
%endif # with tests
%if 0%{?rhel} <= 8
Requires: python3-dataclasses
%endif
Requires: python%{python3_pkgversion}-click
Requires: python%{python3_pkgversion}-rich
Obsoletes: python36-config
Conflicts: python36-config

Expand All@@ -59,17 +68,22 @@ make PYTHON=%{__python3} check

%files -n python%{python3_pkgversion}-config
%defattr(-,root,root,-)
%{python3_sitelib}/python_config.py
%{python3_sitelib}/__pycache__/python_config.*.py*
%{_bindir}/python-config-view
%{python3_sitelib}/python_config/
%{python3_sitelib}/python_config/__pycache__/
%{python3_sitelib}/python_config-%{version}-*.egg-info
%doc ChangeLog INSTALL README
%doc ChangeLog INSTALL README.rst


%clean
[ "%buildroot" = "/" ] || rm -rf "%buildroot"


%changelog
* Tue May 19 2026 Pavel Kulyov <kulyov.pavel@gmail.com> - 1.0.0-1
- Version 1.0.0: AST-based config parsing (no exec), add loads/dump/dumps
- Restructure the project splitting single-module, update metadata

* Tue Jan 24 2023 Andrey Kulaev <adkulaev@gmail.com> - 0.1.2-4
- Add centos 8.4 support

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
*.egg-info
__pycache__
*.pyc

uv.lock
.venv
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,6 @@ include ChangeLog
include INSTALL
include Makefile
include python-config.spec
include README
include README.rst
include tests/*.conf
include tests/*.py
2 changes: 1 addition & 1 deletion Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@ build:
$(PYTHON) setup.py build

check:
$(PYTHON) setup.py test
$(PYTHON) -m pytest tests

install:
$(PYTHON) setup.py install --skip-build $(INSTALL_FLAGS)
Expand Down
8 changes: 0 additions & 8 deletions README

This file was deleted.

88 changes: 88 additions & 0 deletions README.rst
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
A library for reading Python configuration files
================================================

About
-----

This library reads configuration files that are represented as Python modules
with restricted syntax. It uses the AST for parsing.
Values can be written back with ``dump`` and ``dumps``.

Two APIs are available:

* ``python_config.load(s)`` / ``dump(s)`` — read and write a **dict** (lowercase keys).

* ``python_config.document.load(s)`` / ``dump(s)`` — read and write a
:class:`~python_config.document.ConfigDocument`. Use this to change option
values from code and save the file back. Top-level options use uppercase names
(e.g. ``doc["LOG_LEVEL"] = 5``). Nested ``list`` and ``dict`` values are
plain Python objects; in-place mutations are reflected on dump.

Supported value types are:
- ``bool``,
- ``int``,
- ``float``,
- ``str``,
- ``list``,
- ``dict`` (with string keys).

Only **uppercase** names that do not start with ``_`` are exposed through the dict API.

Expressions in source files are **evaluated on load**:

* Arithmetic (e.g. ``COUNT = 1 + 2 * 3``)
* F-strings that reference earlier options (e.g. ``ENDPOINT = f"{HOST}:{PORT}"``)

``dump`` and ``dumps`` always emit **literal values** with aggressive formatting applied.
Hand-edited spacing and expression forms are not preserved on save.

Preamble comments, a module-level docstring, and per-assignment docstrings are
preserved through the document API.

.. NOTE::

If you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project or just use Pydantic.

Interactive viewer
------------------

A terminal UI is included for browsing large configuration files without scrolling
through thousands of lines in an editor. It loads configs through the document API
so f-strings, arithmetic, and comments are shown as in the source.

.. source:: bash

python-config-view /path/to/config.conf
python -m python_config.viewer /path/to/config.conf

Use ``--page-size`` to control pagination (default 20) and ``--no-color`` for
plain output.

Key bindings:

========== ======================================================
Key Action
========== ======================================================
``/`` Search variables or keys at the current level
``n``/``j`` Next item (or next page at end of page)
``p``/``k`` Previous item (or previous page at start of page)
``Enter`` Open a dict/list child or jump to a search result
``1``-``9`` Quick-select an item on the current page
``b`` Go back (or leave search results)
``s`` Expand the current value full-screen (scroll with ``j``/``n`` and ``k``/``p``)
``a`` Toggle assignment source view (top-level variables only)
``q`` Quit
========== ======================================================

Tests
-----

It's recommended to test against python3.6. Assuming here you have .venv36 for this.

.. source:: bash

poe test

# python 3.6
.venv36/bin/pytest -s -vvv tests/
84 changes: 84 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "python-config"
dynamic = ["version"]
description = "A library for reading Python configuration files"
authors = [
{ name = "Dmitry Konishchev", email = "konishchev@gmail.com" },
]
license = { text = "GPL-3.0" }
readme = "README.rst"

keywords = ["python-syntax", "configuration"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX",
"Operating System :: Unix",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython",
]

requires-python = ">=3.9"

dependencies = [
"click>=7,<9",
"rich>=10,<13",
]

[project.scripts]
python-config-view = "python_config.viewer.cli:main"

[project.optional-dependencies]
dev = [
"mypy>=1.19.1",
"poethepoet>=0.32",
"pytest>=8",
"ruff>=0.9",
]

[tool.setuptools.packages.find]
include = ["python_config", "python_config.*"]

[tool.setuptools.dynamic]
version = {file = "version.txt"}

[tool.poe.tasks.format]
help = "Format all the code."
shell = "ruff check --fix --select I python_config/ tests/ && ruff format python_config/ tests/"

[tool.poe.tasks.lint]
help = "Lint all the code."
cmd = "ruff check python_config/ tests/"

[tool.poe.tasks.typecheck]
help = "Typecheck all the code."
cmd = "mypy python_config"

[tool.poe.tasks.test]
help = "Run unit tests."
cmd = "pytest --strict-markers -vvv tests/"

[tool.poe.tasks.ci]
help = "Run full CI sequence."
sequence = [
"format",
"lint",
"typecheck",
"test",
]

[tool.ruff]
line-length = 100

[tool.ruff.lint.isort]
lines-after-imports = 2
26 changes: 20 additions & 6 deletions python-config.spec
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,6 @@

%bcond_without tests

%global project_name pcore
%global project_description %{expand:
Python configuration files themselves are actual Python files. The module
reads only values in uppercase from them, checks that they contain only basic
Expand All@@ -13,8 +12,8 @@ Note: if you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project.}

Name: python-config
Version: 0.1.2
Release: 4.ROCKIT3%{?dist}
Version: 1.0.0
Release: ROCKIT1%{?dist}
Summary: A simple module for reading Python configuration files

Group: Development/Libraries
Expand All@@ -34,7 +33,17 @@ BuildRequires: python%{python3_pkgversion}-devel
BuildRequires: python%{python3_pkgversion}-setuptools
%if 0%{with tests}
BuildRequires: python%{python3_pkgversion}-pytest >= 2.2.4
BuildRequires: python%{python3_pkgversion}-click
BuildRequires: python%{python3_pkgversion}-rich
%if 0%{?rhel} <= 8
BuildRequires: python3-dataclasses
%endif
%endif # with tests
%if 0%{?rhel} <= 8
Requires: python3-dataclasses
%endif
Requires: python%{python3_pkgversion}-click
Requires: python%{python3_pkgversion}-rich
Obsoletes: python36-config
Conflicts: python36-config

Expand All@@ -59,17 +68,22 @@ make PYTHON=%{__python3} check

%files -n python%{python3_pkgversion}-config
%defattr(-,root,root,-)
%{python3_sitelib}/python_config.py
%{python3_sitelib}/__pycache__/python_config.*.py*
%{_bindir}/python-config-view
%{python3_sitelib}/python_config/
%{python3_sitelib}/python_config/__pycache__/
%{python3_sitelib}/python_config-%{version}-*.egg-info
%doc ChangeLog INSTALL README
%doc ChangeLog INSTALL README.rst


%clean
[ "%buildroot" = "/" ] || rm -rf "%buildroot"


%changelog
* Tue May 19 2026 Pavel Kulyov <kulyov.pavel@gmail.com> - 1.0.0-1
- Version 1.0.0: AST-based config parsing (no exec), add loads/dump/dumps
- Restructure the project splitting single-module, update metadata

* Tue Jan 24 2023 Andrey Kulaev <adkulaev@gmail.com> - 0.1.2-4
- Add centos 8.4 support

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
*.egg-info
__pycache__
*.pyc

uv.lock
.venv
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,6 @@ include ChangeLog
include INSTALL
include Makefile
include python-config.spec
include README
include README.rst
include tests/*.conf
include tests/*.py
2 changes: 1 addition & 1 deletion Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@ build:
$(PYTHON) setup.py build

check:
$(PYTHON) setup.py test
$(PYTHON) -m pytest tests

install:
$(PYTHON) setup.py install --skip-build $(INSTALL_FLAGS)
Expand Down
8 changes: 0 additions & 8 deletions README

This file was deleted.

88 changes: 88 additions & 0 deletions README.rst
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
A library for reading Python configuration files
================================================

About
-----

This library reads configuration files that are represented as Python modules
with restricted syntax. It uses the AST for parsing.
Values can be written back with ``dump`` and ``dumps``.

Two APIs are available:

* ``python_config.load(s)`` / ``dump(s)`` — read and write a **dict** (lowercase keys).

* ``python_config.document.load(s)`` / ``dump(s)`` — read and write a
:class:`~python_config.document.ConfigDocument`. Use this to change option
values from code and save the file back. Top-level options use uppercase names
(e.g. ``doc["LOG_LEVEL"] = 5``). Nested ``list`` and ``dict`` values are
plain Python objects; in-place mutations are reflected on dump.

Supported value types are:
- ``bool``,
- ``int``,
- ``float``,
- ``str``,
- ``list``,
- ``dict`` (with string keys).

Only **uppercase** names that do not start with ``_`` are exposed through the dict API.

Expressions in source files are **evaluated on load**:

* Arithmetic (e.g. ``COUNT = 1 + 2 * 3``)
* F-strings that reference earlier options (e.g. ``ENDPOINT = f"{HOST}:{PORT}"``)

``dump`` and ``dumps`` always emit **literal values** with aggressive formatting applied.
Hand-edited spacing and expression forms are not preserved on save.

Preamble comments, a module-level docstring, and per-assignment docstrings are
preserved through the document API.

.. NOTE::

If you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project or just use Pydantic.

Interactive viewer
------------------

A terminal UI is included for browsing large configuration files without scrolling
through thousands of lines in an editor. It loads configs through the document API
so f-strings, arithmetic, and comments are shown as in the source.

.. source:: bash

python-config-view /path/to/config.conf
python -m python_config.viewer /path/to/config.conf

Use ``--page-size`` to control pagination (default 20) and ``--no-color`` for
plain output.

Key bindings:

========== ======================================================
Key Action
========== ======================================================
``/`` Search variables or keys at the current level
``n``/``j`` Next item (or next page at end of page)
``p``/``k`` Previous item (or previous page at start of page)
``Enter`` Open a dict/list child or jump to a search result
``1``-``9`` Quick-select an item on the current page
``b`` Go back (or leave search results)
``s`` Expand the current value full-screen (scroll with ``j``/``n`` and ``k``/``p``)
``a`` Toggle assignment source view (top-level variables only)
``q`` Quit
========== ======================================================

Tests
-----

It's recommended to test against python3.6. Assuming here you have .venv36 for this.

.. source:: bash

poe test

# python 3.6
.venv36/bin/pytest -s -vvv tests/
84 changes: 84 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "python-config"
dynamic = ["version"]
description = "A library for reading Python configuration files"
authors = [
{ name = "Dmitry Konishchev", email = "konishchev@gmail.com" },
]
license = { text = "GPL-3.0" }
readme = "README.rst"

keywords = ["python-syntax", "configuration"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX",
"Operating System :: Unix",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython",
]

requires-python = ">=3.9"

dependencies = [
"click>=7,<9",
"rich>=10,<13",
]

[project.scripts]
python-config-view = "python_config.viewer.cli:main"

[project.optional-dependencies]
dev = [
"mypy>=1.19.1",
"poethepoet>=0.32",
"pytest>=8",
"ruff>=0.9",
]

[tool.setuptools.packages.find]
include = ["python_config", "python_config.*"]

[tool.setuptools.dynamic]
version = {file = "version.txt"}

[tool.poe.tasks.format]
help = "Format all the code."
shell = "ruff check --fix --select I python_config/ tests/ && ruff format python_config/ tests/"

[tool.poe.tasks.lint]
help = "Lint all the code."
cmd = "ruff check python_config/ tests/"

[tool.poe.tasks.typecheck]
help = "Typecheck all the code."
cmd = "mypy python_config"

[tool.poe.tasks.test]
help = "Run unit tests."
cmd = "pytest --strict-markers -vvv tests/"

[tool.poe.tasks.ci]
help = "Run full CI sequence."
sequence = [
"format",
"lint",
"typecheck",
"test",
]

[tool.ruff]
line-length = 100

[tool.ruff.lint.isort]
lines-after-imports = 2
26 changes: 20 additions & 6 deletions python-config.spec
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,6 @@

%bcond_without tests

%global project_name pcore
%global project_description %{expand:
Python configuration files themselves are actual Python files. The module
reads only values in uppercase from them, checks that they contain only basic
Expand All@@ -13,8 +12,8 @@ Note: if you want to validate the configuration values, take a look at
https://github.com/KonishchevDmitry/object-validator project.}

Name: python-config
Version: 0.1.2
Release: 4.ROCKIT3%{?dist}
Version: 1.0.0
Release: ROCKIT1%{?dist}
Summary: A simple module for reading Python configuration files

Group: Development/Libraries
Expand All@@ -34,7 +33,17 @@ BuildRequires: python%{python3_pkgversion}-devel
BuildRequires: python%{python3_pkgversion}-setuptools
%if 0%{with tests}
BuildRequires: python%{python3_pkgversion}-pytest >= 2.2.4
BuildRequires: python%{python3_pkgversion}-click
BuildRequires: python%{python3_pkgversion}-rich
%if 0%{?rhel} <= 8
BuildRequires: python3-dataclasses
%endif
%endif # with tests
%if 0%{?rhel} <= 8
Requires: python3-dataclasses
%endif
Requires: python%{python3_pkgversion}-click
Requires: python%{python3_pkgversion}-rich
Obsoletes: python36-config
Conflicts: python36-config

Expand All@@ -59,17 +68,22 @@ make PYTHON=%{__python3} check

%files -n python%{python3_pkgversion}-config
%defattr(-,root,root,-)
%{python3_sitelib}/python_config.py
%{python3_sitelib}/__pycache__/python_config.*.py*
%{_bindir}/python-config-view
%{python3_sitelib}/python_config/
%{python3_sitelib}/python_config/__pycache__/
%{python3_sitelib}/python_config-%{version}-*.egg-info
%doc ChangeLog INSTALL README
%doc ChangeLog INSTALL README.rst


%clean
[ "%buildroot" = "/" ] || rm -rf "%buildroot"


%changelog
* Tue May 19 2026 Pavel Kulyov <kulyov.pavel@gmail.com> - 1.0.0-1
- Version 1.0.0: AST-based config parsing (no exec), add loads/dump/dumps
- Restructure the project splitting single-module, update metadata

* Tue Jan 24 2023 Andrey Kulaev <adkulaev@gmail.com> - 0.1.2-4
- Add centos 8.4 support

Expand Down
Loading