From 4fa06032f619739c60ad124208a89fffaf28107f Mon Sep 17 00:00:00 2001 From: Stephane Robert Date: Thu, 31 Oct 2024 14:42:35 -0400 Subject: [PATCH 1/2] updating code to python 3.12 --- config_probe/__init__.py | 8 ++++---- test-requirements.txt | 3 ++- tox.ini | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/config_probe/__init__.py b/config_probe/__init__.py index a05f901..e2879bc 100644 --- a/config_probe/__init__.py +++ b/config_probe/__init__.py @@ -4,7 +4,7 @@ import os import yaml -from munch import Munch, iteritems +from munch import Munch from config_probe.exceptions import ConfigNotFound @@ -46,7 +46,7 @@ def _deduce_namespaces(path_matchers, path_parts): namespaces = [] path_parts, current_part = os.path.split(path_parts) path_matchers, matcher = os.path.split(path_matchers) - while current_part is not "": + while current_part != "": if matcher == NAMESPACE_PLACEHOLDER: namespaces.append(current_part) @@ -67,7 +67,7 @@ def _add_to_configuration(config, namespaces, new_values): def _update(config, values): for k, v in values.items(): - if k in config and isinstance(v, collections.Mapping): + if k in config and isinstance(v, collections.abc.Mapping): _update(config[k], v) else: config[k] = v @@ -83,7 +83,7 @@ def __getattr__(self, k): def _munchify(x): if isinstance(x, dict): - return _Munch((k, _munchify(v)) for k,v in iteritems(x)) + return _Munch((k, _munchify(v)) for k,v in x.items()) elif isinstance(x, (list, tuple)): return type(x)(_munchify(v) for v in x) else: diff --git a/test-requirements.txt b/test-requirements.txt index 3c289d7..106473a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,2 +1,3 @@ -nose +pytest +pytest-cov pyhamcrest diff --git a/tox.ini b/tox.ini index bbdb67d..dc5b007 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] -envlist = py27,py34 +envlist = py34,p312 [testenv] deps = -r{toxinidir}/test-requirements.txt -commands = nosetests +commands = pytest --cov --cov-report term-missing From 84c49195e488471efef24e1d41be48c3f3bd5e82 Mon Sep 17 00:00:00 2001 From: stephanerobert Date: Fri, 1 Nov 2024 10:24:17 -0400 Subject: [PATCH 2/2] Create publish.yml --- .github/workflows/publish.yml | 28 ++++++++++++++ .github/workflows/tox.yml | 18 +++++++++ .travis.yml | 21 ---------- README.md | 13 +++---- config-mixin.iml | 9 +++++ {config_probe => config_mixin}/__init__.py | 8 +++- {config_probe => config_mixin}/exceptions.py | 0 pyproject.toml | 38 +++++++++++++++++++ setup.cfg | 28 -------------- setup.py | 8 ---- ...t_config_probe.py => test_config_mixin.py} | 36 +++++++++--------- tox.ini | 6 ++- 12 files changed, 128 insertions(+), 85 deletions(-) create mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/tox.yml delete mode 100644 .travis.yml create mode 100644 config-mixin.iml rename {config_probe => config_mixin}/__init__.py (94%) rename {config_probe => config_mixin}/exceptions.py (100%) create mode 100644 pyproject.toml delete mode 100644 setup.cfg delete mode 100644 setup.py rename tests/{test_config_probe.py => test_config_mixin.py} (82%) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..f6e4906 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,28 @@ +name: Upload Python Package + +on: + release: + types: [published] +jobs: + build-and-publish: + runs-on: ubuntu-latest + environment: pypi + permissions: + # IMPORTANT: this permission is mandatory for trusted publishing + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.release.tag_name }} + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + check-latest: true + - name: Install build dependencies + run: pip install -U setuptools wheel build + - name: Build + run: python -m build . + - name: Publish + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml new file mode 100644 index 0000000..46d945c --- /dev/null +++ b/.github/workflows/tox.yml @@ -0,0 +1,18 @@ +name: Test + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Python environment + uses: actions/setup-python@v3 + with: + python-version: '3.12' + - name: tox + run: | + python -m pip install --upgrade pip + python -m pip install tox + tox diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7659bd5..0000000 --- a/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -language: python - -python: - - "2.7" - - "3.4" - -install: - - pip install -r requirements.txt - - pip install -r test-requirements.txt - -script: nosetests - -deploy: - provider: pypi - user: internaphosting - password: - secure: nztUy6LTQreUCivL/g+8PFaFkSKPvAEJEe5i/qvfvJky4j6H5gCdnUl3SULRD2xaNztE/kWiT1hs4PqThR8isoay0gB7/8YhVeoMjMHjvi92LXcNu5zsvNpZMq68XKQU8c4nFCwkBtPdu7IumBuow0OmohnLFb0/zTC13U20dZ+F4PMD842e4SgXtXlwjF5OROVi8Djz7CeLxti3O6FytPCaljN3I/a+qtlxbkI+SUXTGTyZNwatoYdt/UI7O+JfvjT79MDTzQhpfB+O1+z4KhYsTcaXrwLGhggA5VKu10ykBBiRqU9neJ1LJvqdxCpjLjKRcohurrRgmRhtBK4v9+M9bKMsqGOmG15YsJrjhXbahvzjx13cpPds6a1Bf1Hs2Ny1l3SjxRKlRyEQKyhufg4td6MzrzfZyNXTbEk601StSbCWV8Mdj3nVdfS3of2yNYIHeSRaioPuJAZrtb/qnN73n6k44UZbw/p6Tk6Tf1wtCqQI+sHAeG2NVaHp6HDxd5Qdg1Zp2XLxE8GnbkCyuFzjxxIMCrUnv1UBo4B+3xj6FMfINOJs6cCHISvvc0icJEmqIhVZwOicd9Dywj71IvdFDZ1xGR5ehR8M7fCqP+UXBVf+SnYTZPcz9cP+fTCBuLpixiFpsX0qPL75dRHb8X0DoOUuVgHky1VKquNay14= - on: - tags: true - python: 3.4 - repo: internap/python-config-probe diff --git a/README.md b/README.md index 2408c48..9e04f86 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ -[![Build Status](https://travis-ci.org/internap/python-config-probe.svg?branch=master)](https://travis-ci.org/internap/python-config-probe) -[![PyPI version](https://badge.fury.io/py/config-probe.svg)](http://badge.fury.io/py/config-probe) - +![Build Status](https://github.com/stephanerobert/config-mixin/actions/workflows/tox.yml/badge.svg?branch=master) +[![PyPI version](https://badge.fury.io/py/config-mixin.svg?icon=si%3Apython)](https://badge.fury.io/py/config-mixin) Mission ======= @@ -12,7 +11,7 @@ the result config will be a shortcut to any config. Setup: - config = probe( + config = mixin( path="path/to/my/files", patterns=["path/(*)/file.yaml"] ) @@ -25,7 +24,7 @@ Use it: - **path** - Initial path to probe. Patterns will be tested against the file structure underneath the path + Initial path to configs. Patterns will be tested against the file structure underneath the path, and it will be ignored in determining the namespacing. - **patterns** @@ -58,8 +57,8 @@ Use it: ## Mocking the probing -Your unit test can have your code use fake_probe instead to which to give a dict and it will appear as if it -was just probed. Example: +Your unit test can have your code use fake_probe instead to which to give a dict, and it will appear as if it +was just mixed-in. Example: config = fake_probe({ "ns1": { diff --git a/config-mixin.iml b/config-mixin.iml new file mode 100644 index 0000000..ad3c0a3 --- /dev/null +++ b/config-mixin.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/config_probe/__init__.py b/config_mixin/__init__.py similarity index 94% rename from config_probe/__init__.py rename to config_mixin/__init__.py index e2879bc..1b5723b 100644 --- a/config_probe/__init__.py +++ b/config_mixin/__init__.py @@ -1,3 +1,5 @@ +__version__ = "1.0.0" + import collections import glob import json @@ -6,12 +8,16 @@ import yaml from munch import Munch -from config_probe.exceptions import ConfigNotFound +from config_mixin.exceptions import ConfigNotFound NAMESPACE_PLACEHOLDER = "(*)" def probe(path, patterns): + return mixin(path, patterns) + + +def mixin(path, patterns): config = {} for pattern in patterns: diff --git a/config_probe/exceptions.py b/config_mixin/exceptions.py similarity index 100% rename from config_probe/exceptions.py rename to config_mixin/exceptions.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d0672a3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,38 @@ +[build-system] +requires = ["setuptools>=61.0", "setuptools-scm"] +build-backend = "setuptools.build_meta" + +[project] +name = "config_mixin" +dynamic = ["version"] +description = "Provide an auto-discovery process for configurations" +readme = "README.md" +requires-python = ">=3.10" +license = {text = "License :: OSI Approved :: Apache Software License"} +classifiers = [ + "Intended Audience :: Developers", + "Development Status :: 4 - Beta", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12" +] +dependencies = [ + "munch", + "PyYAML" +] +authors = [ + {name = "Stephane Robert", email = "stephane.robert@gmail.com"}, +] + +[project.optional-dependencies] +test = ["pytest", "pytest-cov", "pyhamcrest"] + +[project.urls] +"Homepage" = "https://github.com/stephanerobert/config-mixin" + +[tool.setuptools.dynamic] +version = {attr = "config_mixin.__version__"} diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index d9184fb..0000000 --- a/setup.cfg +++ /dev/null @@ -1,28 +0,0 @@ -[metadata] -name = config-probe -url = https://github.com/internap/python-config-probe -author = Internap Hosting -author-email = opensource@internap.com -summary = Auto-discovery of configurations for easy inline use -description-file = - README.md -classifier = - Development Status :: 4 - Beta - Intended Audience :: Developers - Intended Audience :: Information Technology - Intended Audience :: System Administrators - Intended Audience :: Telecommunications Industry - License :: OSI Approved :: Apache Software License - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3.4 - -[files] -packages = - config_probe - -[bdist_wheel] -universal = 1 - -[nosetests] -no-path-adjustment = 1 -logging-level = DEBUG diff --git a/setup.py b/setup.py deleted file mode 100644 index aa2d8a0..0000000 --- a/setup.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python - -from setuptools import setup - -setup( - setup_requires=['pbr'], - pbr=True, -) diff --git a/tests/test_config_probe.py b/tests/test_config_mixin.py similarity index 82% rename from tests/test_config_probe.py rename to tests/test_config_mixin.py index 229967c..921e55c 100644 --- a/tests/test_config_probe.py +++ b/tests/test_config_mixin.py @@ -1,34 +1,34 @@ import unittest import os -from config_probe import probe, fake_probe -from config_probe.exceptions import ConfigNotFound +from config_mixin import mixin, fake_probe +from config_mixin.exceptions import ConfigNotFound from hamcrest import is_, assert_that, has_key class TestConfigProbe(unittest.TestCase): def test_single_file(self): - config = probe(path=_dir("single-file"), + config = mixin(path=_dir("single-file"), patterns=["stuff.yaml"]) assert_that(config.key, is_("stuff-value")) assert_that(config.fruits, is_(["Apple", "Orange"])) def test_single_file_with_namespace(self): - config = probe(path=_dir("single-file-with-namespace"), + config = mixin(path=_dir("single-file-with-namespace"), patterns=["(*).json"]) assert_that(config.stuff.key, is_("stuff-value")) def test_single_file_with_namespace_with_wrong_key(self): - config = probe(path=_dir("single-file-with-namespace"), + config = mixin(path=_dir("single-file-with-namespace"), patterns=["(*).json"]) with self.assertRaises(ConfigNotFound): print(config.stuff.key_inexistant) def test_single_file_multiple_level_raising_or_not(self): - config = probe(path=_dir("multi-level-files"), + config = mixin(path=_dir("multi-level-files"), patterns=["(*)/(*).yaml", "(*)/subdir/(*).yaml"]) assert_that(config.ns1.stuff.we.need.more.cowbell, is_("ok")) @@ -36,51 +36,51 @@ def test_single_file_multiple_level_raising_or_not(self): print(config.ns1.stuff.we.need.less.cowbell) def test_two_files_with_subdir_namespace(self): - config = probe(path=_dir("two-files-with-subdir-namespace"), + config = mixin(path=_dir("two-files-with-subdir-namespace"), patterns=["(*)/(*).yaml"]) assert_that(config.ns1.stuff.key1, is_("stuff from ns1")) assert_that(config.ns2.stuff.key2, is_("stuff from ns2")) def test_only_starred_parts_are_namespaced(self): - config = probe(path=_dir("two-files-with-subdir-namespace"), + config = mixin(path=_dir("two-files-with-subdir-namespace"), patterns=["(*)/stuff.yaml"]) assert_that(config.ns1.key1, is_("stuff from ns1")) assert_that(config.ns2.key2, is_("stuff from ns2")) def test_using_only_a_star_does_not_count_toward_namespacing(self): - config = probe(path=_dir("two-files-with-subdir-namespace"), + config = mixin(path=_dir("two-files-with-subdir-namespace"), patterns=["*/stuff.yaml"]) assert_that(config.key1, is_("stuff from ns1")) assert_that(config.key2, is_("stuff from ns2")) def test_multiple_patterns(self): - config = probe(path=_dir("two-files-with-subdir-namespace"), + config = mixin(path=_dir("two-files-with-subdir-namespace"), patterns=["ns1/(*).yaml", "(*)/stuff.yaml"]) assert_that(config.stuff.key1, is_("stuff from ns1")) assert_that(config.ns2.key2, is_("stuff from ns2")) def test_multiple_patterns_on_same_namespaces_should_merge_recursively(self): - config = probe(path=_dir("multi-level-files"), + config = mixin(path=_dir("multi-level-files"), patterns=["(*)/(*).yaml", "(*)/subdir/(*).yaml"]) assert_that(config.ns1.stuff.content1.key1, is_("value1")) assert_that(config.ns1.stuff.content2.key2, is_("value2")) def test_pattern_order_defines_which_files_have_the_authority(self): - config = probe(path=_dir("key-override"), + config = mixin(path=_dir("key-override"), patterns=["file1.yaml", "file2.yaml"]) assert_that(config.key, is_("value2")) - config = probe(path=_dir("key-override"), + config = mixin(path=_dir("key-override"), patterns=["file2.yaml", "file1.yaml"]) assert_that(config.key, is_("value1")) def test_yaml_dict_are_merged_list_arent(self): - config = probe(path=_dir("dict-merge"), + config = mixin(path=_dir("dict-merge"), patterns=["file1.yaml", "file2.yaml"]) assert_that(config.mydict.common_key, is_("value2")) assert_that(config.mydict.only_in_1, is_("value1")) @@ -89,7 +89,7 @@ def test_yaml_dict_are_merged_list_arent(self): assert_that(config.mydict.subdict.only_in_1, is_("value1")) assert_that(config.mydict.subdict.only_in_2, is_("value2")) - config = probe(path=_dir("dict-merge"), + config = mixin(path=_dir("dict-merge"), patterns=["file2.yaml", "file1.yaml"]) assert_that(config.mydict.common_key, is_("value1")) assert_that(config.mydict.only_in_1, is_("value1")) @@ -99,7 +99,7 @@ def test_yaml_dict_are_merged_list_arent(self): assert_that(config.mydict.subdict.only_in_2, is_("value2")) def test_dict_should_behave_as_dict(self): - config = probe(path=_dir("single-file"), + config = mixin(path=_dir("single-file"), patterns=["(*).yaml"]) assert_that(config["stuff"]["key"], is_("stuff-value")) @@ -109,10 +109,10 @@ def test_dict_should_behave_as_dict(self): assert_that(dict(**config.stuff), has_key('key')) def test_support_for_empty_files(self): - probe(path=_dir("empty-files"), patterns=["*.*"]) + mixin(path=_dir("empty-files"), patterns=["*.*"]) def test_support_absolute_paths_outside_base_dir(self): - config = probe(path=_dir("single-file-with-namespace"), patterns=[ + config = mixin(path=_dir("single-file-with-namespace"), patterns=[ "{}/(*)/(*)/stuff.yaml".format(os.path.dirname(__file__)) ]) assert_that(config['two-files-with-subdir-namespace'].ns1.key1, is_("stuff from ns1")) diff --git a/tox.ini b/tox.ini index dc5b007..2360003 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,8 @@ [tox] -envlist = py34,p312 +envlist = py312 [testenv] -deps = -r{toxinidir}/test-requirements.txt +deps = + -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt commands = pytest --cov --cov-report term-missing