Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-pycomposefile for
openSUSE:Factory checked in at 2026-01-12 11:50:13
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pycomposefile (Old)
and /work/SRC/openSUSE:Factory/.python-pycomposefile.new.1928 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pycomposefile"
Mon Jan 12 11:50:13 2026 rev:4 rq:1326740 version:0.0.34
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-pycomposefile/python-pycomposefile.changes
2025-02-14 19:21:11.047407369 +0100
+++
/work/SRC/openSUSE:Factory/.python-pycomposefile.new.1928/python-pycomposefile.changes
2026-01-12 11:50:31.986673104 +0100
@@ -1,0 +2,14 @@
+Fri Jan 9 13:08:45 UTC 2026 - John Paul Adrian Glaubitz
<[email protected]>
+
+- Update to version 0.0.34
+ * Add comprehensive test coverage for KVP splitting with multiple equals
signs
+ * Add LICENSE and sample/ to sdist
+- from version 0.0.33
+ * Bump setuptools from 75.2.0 to 78.1.1
+ * Update service
+ * Fix regex to correctly capture default values in environment variable
+ replacement embed version in sdist and migrate to pyproject.toml
+- Drop p_fix-version-number.patch, fixed upstream
+- Update BuildRequires from pyproject.toml
+
+-------------------------------------------------------------------
Old:
----
p_fix-version-number.patch
pycomposefile-0.0.32.tar.gz
New:
----
pycomposefile-0.0.34.tar.gz
----------(Old B)----------
Old: replacement embed version in sdist and migrate to pyproject.toml
- Drop p_fix-version-number.patch, fixed upstream
- Update BuildRequires from pyproject.toml
----------(Old E)----------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pycomposefile.spec ++++++
--- /var/tmp/diff_new_pack.F0A0jR/_old 2026-01-12 11:50:32.826707904 +0100
+++ /var/tmp/diff_new_pack.F0A0jR/_new 2026-01-12 11:50:32.826707904 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-pycomposefile
#
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,15 +18,13 @@
%{?sle15_python_module_pythons}
Name: python-pycomposefile
-Version: 0.0.32
+Version: 0.0.34
Release: 0
Summary: Structured deserialization of Docker Compose files
License: MIT
URL: https://github.com/smurawski/pycomposefile
Source:
https://files.pythonhosted.org/packages/source/p/pycomposefile/pycomposefile-%{version}.tar.gz
-# PATCH-FIX-UPSTREAM p_fix-version-number.patch
-# https://github.com/smurawski/pycomposefile/issues/29
-Patch: p_fix-version-number.patch
+BuildRequires: %{python_module flit-core >= 3.11}
BuildRequires: %{python_module pip}
BuildRequires: %{python_module setuptools}
BuildRequires: %{python_module wheel}
++++++ pycomposefile-0.0.32.tar.gz -> pycomposefile-0.0.34.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycomposefile-0.0.32/LICENSE
new/pycomposefile-0.0.34/LICENSE
--- old/pycomposefile-0.0.32/LICENSE 1970-01-01 01:00:00.000000000 +0100
+++ new/pycomposefile-0.0.34/LICENSE 2025-07-29 16:13:25.845282800 +0200
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Steven Murawski
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycomposefile-0.0.32/PKG-INFO
new/pycomposefile-0.0.34/PKG-INFO
--- old/pycomposefile-0.0.32/PKG-INFO 2024-10-22 21:45:10.409924300 +0200
+++ new/pycomposefile-0.0.34/PKG-INFO 1970-01-01 01:00:00.000000000 +0100
@@ -1,13 +1,15 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
Name: pycomposefile
-Version: 0.0.32
+Version: 0.0.34
Summary: Structured deserialization of Docker Compose files.
-Home-page: https://github.com/smurawski/pycomposefile
-Author: Steven Murawski
-Author-email: [email protected]
-License: MIT
-Requires-Python: >=3.6
+Keywords: docker,compose
+Author-email: Steven Murawski <[email protected]>
+Requires-Python: >=3.9
Description-Content-Type: text/markdown
+License-Expression: MIT
+License-File: LICENSE
Requires-Dist: pyyaml
+Project-URL: Homepage, https://github.com/smurawski/pycomposefile
# Pycomposefile
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/pycomposefile/compose_element/compose_datatype_transformer.py
new/pycomposefile-0.0.34/pycomposefile/compose_element/compose_datatype_transformer.py
---
old/pycomposefile-0.0.32/pycomposefile/compose_element/compose_datatype_transformer.py
2024-10-22 21:45:01.000000000 +0200
+++
new/pycomposefile-0.0.34/pycomposefile/compose_element/compose_datatype_transformer.py
2025-07-29 16:13:25.845282800 +0200
@@ -11,7 +11,7 @@
return f"Failed to evaluate mandatory variable {self.variable_name}"
-class ComposeDataTypeTransformer():
+class ComposeDataTypeTransformer:
transform = str
valid_values = None
@@ -59,7 +59,7 @@
return re.sub(env_variable_name, env_variable_value, source_string)
def replace_environment_variables_with_empty_unset(self, value):
- capture =
re.compile(r"(\$+)\{(?P<variablename>\w+)\:-(?P<defaultvalue>.+)\}")
+ capture =
re.compile(r"(\$+)\{(?P<variablename>\w+)\:-(?P<defaultvalue>[^$]*?)\}")
matches = capture.search(value)
if matches is not None and matches[1] == "$$":
return value
@@ -68,7 +68,8 @@
default_value = matches.group("defaultvalue")
if env_var is None or len(env_var) == 0:
env_var = default_value
- value =
self.update_value_with_resolved_environment(f"\\{matches[0]}", env_var, value)
+ escaped = re.sub(r"([*+?{}])", r"\\\1", matches[0]) # escape
special characters
+ value =
self.update_value_with_resolved_environment(f"\\{escaped}", env_var, value)
matches = capture.search(value)
return value
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/pycomposefile/secrets/__init__.py
new/pycomposefile-0.0.34/pycomposefile/secrets/__init__.py
--- old/pycomposefile-0.0.32/pycomposefile/secrets/__init__.py 2024-10-22
21:45:01.000000000 +0200
+++ new/pycomposefile-0.0.34/pycomposefile/secrets/__init__.py 2025-07-29
16:13:25.846283000 +0200
@@ -3,7 +3,7 @@
class SecretFile(str):
def readFile(self):
- with open(self, "r") as f:
+ with open(self) as f:
secret = f.read()
return secret
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/pycomposefile/service/service.py
new/pycomposefile-0.0.34/pycomposefile/service/service.py
--- old/pycomposefile-0.0.32/pycomposefile/service/service.py 2024-10-22
21:45:01.000000000 +0200
+++ new/pycomposefile-0.0.34/pycomposefile/service/service.py 2025-07-29
16:13:25.846283000 +0200
@@ -91,7 +91,6 @@
"networks": (Networks,
"https://github.com/compose-spec/compose-spec/blob/master/spec.md#networks"),
"mac_address": (str,
"https://github.com/compose-spec/compose-spec/blob/master/spec.md#mac_address"),
"mem_limit": (ComposeByteValue,
"https://github.com/compose-spec/compose-spec/blob/master/spec.md#mem_limit"),
- "mem_reservation": (ComposeByteValue,
"https://github.com/compose-spec/compose-spec/blob/master/spec.md#mem_reservation"),
"mem_swappiness": ((int, [0, 100]),
"https://github.com/compose-spec/compose-spec/blob/master/spec.md#mem_swappiness"),
"memswap_limit": (ComposeByteValue,
"https://github.com/compose-spec/compose-spec/blob/master/spec.md#memswap_limit"),
"oom_kill_disable": (bool,
"https://github.com/compose-spec/compose-spec/blob/master/spec.md#oom_kill_disable"),
@@ -138,7 +137,7 @@
def resolve_environment_hierarchy(self):
if self.env_file is not None:
env_file = self.env_file.readFile()
- env_file.update(self.environment)
+ env_file.update(self.environment or [])
return env_file
else:
return self.environment
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/pycomposefile/service/service_environment.py
new/pycomposefile-0.0.34/pycomposefile/service/service_environment.py
--- old/pycomposefile-0.0.32/pycomposefile/service/service_environment.py
2024-10-22 21:45:01.000000000 +0200
+++ new/pycomposefile-0.0.34/pycomposefile/service/service_environment.py
2025-07-29 16:13:25.846283000 +0200
@@ -11,7 +11,7 @@
env_array = []
for file_name in self:
- f = open(file_name, "r")
+ f = open(file_name)
for line in f.readlines():
if not line.startswith("#"):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/pycomposefile/service/service_misc.py
new/pycomposefile-0.0.34/pycomposefile/service/service_misc.py
--- old/pycomposefile-0.0.32/pycomposefile/service/service_misc.py
2024-10-22 21:45:01.000000000 +0200
+++ new/pycomposefile-0.0.34/pycomposefile/service/service_misc.py
2025-07-29 16:13:25.847283000 +0200
@@ -17,7 +17,7 @@
condition = detail["condition"]
else:
name = config
- ob = super(Dependency, cls).__new__(cls, name)
+ ob = super().__new__(cls, name)
ob.__setattr__('condition', condition)
return ob
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycomposefile-0.0.32/pycomposefile.egg-info/PKG-INFO
new/pycomposefile-0.0.34/pycomposefile.egg-info/PKG-INFO
--- old/pycomposefile-0.0.32/pycomposefile.egg-info/PKG-INFO 2024-10-22
21:45:10.000000000 +0200
+++ new/pycomposefile-0.0.34/pycomposefile.egg-info/PKG-INFO 1970-01-01
01:00:00.000000000 +0100
@@ -1,13 +0,0 @@
-Metadata-Version: 2.1
-Name: pycomposefile
-Version: 0.0.32
-Summary: Structured deserialization of Docker Compose files.
-Home-page: https://github.com/smurawski/pycomposefile
-Author: Steven Murawski
-Author-email: [email protected]
-License: MIT
-Requires-Python: >=3.6
-Description-Content-Type: text/markdown
-Requires-Dist: pyyaml
-
-# Pycomposefile
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/pycomposefile.egg-info/SOURCES.txt
new/pycomposefile-0.0.34/pycomposefile.egg-info/SOURCES.txt
--- old/pycomposefile-0.0.32/pycomposefile.egg-info/SOURCES.txt 2024-10-22
21:45:10.000000000 +0200
+++ new/pycomposefile-0.0.34/pycomposefile.egg-info/SOURCES.txt 1970-01-01
01:00:00.000000000 +0100
@@ -1,50 +0,0 @@
-README.md
-setup.py
-pycomposefile/__init__.py
-pycomposefile/compose_file.py
-pycomposefile.egg-info/PKG-INFO
-pycomposefile.egg-info/SOURCES.txt
-pycomposefile.egg-info/dependency_links.txt
-pycomposefile.egg-info/requires.txt
-pycomposefile.egg-info/top_level.txt
-pycomposefile/compose_element/__init__.py
-pycomposefile/compose_element/compose_datatype_transformer.py
-pycomposefile/compose_element/compose_element.py
-pycomposefile/compose_element/compose_list_or_map.py
-pycomposefile/compose_element/compose_string_or_list.py
-pycomposefile/secrets/__init__.py
-pycomposefile/service/__init__.py
-pycomposefile/service/service.py
-pycomposefile/service/service_blkio_config.py
-pycomposefile/service/service_build.py
-pycomposefile/service/service_cap.py
-pycomposefile/service/service_command.py
-pycomposefile/service/service_configs.py
-pycomposefile/service/service_credential_spec.py
-pycomposefile/service/service_deploy.py
-pycomposefile/service/service_environment.py
-pycomposefile/service/service_healthcheck.py
-pycomposefile/service/service_logging.py
-pycomposefile/service/service_misc.py
-pycomposefile/service/service_networks.py
-pycomposefile/service/service_ports.py
-pycomposefile/service/service_volumes.py
-tests/__init__.py
-tests/compose_file/__init__.py
-tests/compose_file/test_service_order.py
-tests/compose_file/test_version.py
-tests/compose_generator/__init__.py
-tests/environment_variable_evaluation/__init__.py
-tests/environment_variable_evaluation/test_environment_variables_braces.py
-tests/environment_variable_evaluation/test_environment_variables_no_braces.py
-tests/service/__init__.py
-tests/service/test_service.py
-tests/service/test_service_blkio_config.py
-tests/service/test_service_build.py
-tests/service/test_service_configs.py
-tests/service/test_service_credential_spec.py
-tests/service/test_service_deploy.py
-tests/service/test_service_environment.py
-tests/service/test_service_environment_file.py
-tests/service/test_service_network.py
-tests/service/test_service_secret_file.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/pycomposefile.egg-info/dependency_links.txt
new/pycomposefile-0.0.34/pycomposefile.egg-info/dependency_links.txt
--- old/pycomposefile-0.0.32/pycomposefile.egg-info/dependency_links.txt
2024-10-22 21:45:10.000000000 +0200
+++ new/pycomposefile-0.0.34/pycomposefile.egg-info/dependency_links.txt
1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/pycomposefile.egg-info/requires.txt
new/pycomposefile-0.0.34/pycomposefile.egg-info/requires.txt
--- old/pycomposefile-0.0.32/pycomposefile.egg-info/requires.txt
2024-10-22 21:45:10.000000000 +0200
+++ new/pycomposefile-0.0.34/pycomposefile.egg-info/requires.txt
1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-pyyaml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/pycomposefile.egg-info/top_level.txt
new/pycomposefile-0.0.34/pycomposefile.egg-info/top_level.txt
--- old/pycomposefile-0.0.32/pycomposefile.egg-info/top_level.txt
2024-10-22 21:45:10.000000000 +0200
+++ new/pycomposefile-0.0.34/pycomposefile.egg-info/top_level.txt
1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-pycomposefile
-tests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycomposefile-0.0.32/pyproject.toml
new/pycomposefile-0.0.34/pyproject.toml
--- old/pycomposefile-0.0.32/pyproject.toml 1970-01-01 01:00:00.000000000
+0100
+++ new/pycomposefile-0.0.34/pyproject.toml 2025-07-29 16:13:30.947512400
+0200
@@ -0,0 +1,34 @@
+[project]
+name = "pycomposefile"
+version = "0.0.34"
+description = "Structured deserialization of Docker Compose files."
+readme = "README.md"
+authors = [
+ { name = "Steven Murawski", email = "[email protected]" },
+]
+requires-python = ">=3.9"
+dependencies = ["pyyaml"]
+keywords = ["docker", "compose"]
+license = "MIT"
+license-files = ["LICENSE"]
+
+[project.urls]
+Homepage = "https://github.com/smurawski/pycomposefile"
+
+[dependency-groups]
+dev = ["pyyaml>=6.0", "build>=0.7", "ruff==0.12.5", "timeout-decorator==0.5.0"]
+
+[tool.flit.sdist]
+include = ["sample/", "tests/"]
+
+[tool.ruff]
+line-length = 120
+
+[tool.ruff.lint]
+select = ["C9", "E", "F", "UP", "W"]
+ignore = ["C901", "E501", "E722", "F401", "F811"]
+mccabe = { max-complexity = 10 }
+
+[build-system]
+requires = ["flit_core >=3.11,<4"]
+build-backend = "flit_core.buildapi"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycomposefile-0.0.32/sample/apps/web.env
new/pycomposefile-0.0.34/sample/apps/web.env
--- old/pycomposefile-0.0.32/sample/apps/web.env 1970-01-01
01:00:00.000000000 +0100
+++ new/pycomposefile-0.0.34/sample/apps/web.env 2025-07-29
16:13:25.847283000 +0200
@@ -0,0 +1,5 @@
+# Web Environment Vars
+RACK_ENV=development
+VAR="quoted"
+FOO="bar"
+BAZ=foobar
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycomposefile-0.0.32/sample/common.env
new/pycomposefile-0.0.34/sample/common.env
--- old/pycomposefile-0.0.32/sample/common.env 1970-01-01 01:00:00.000000000
+0100
+++ new/pycomposefile-0.0.34/sample/common.env 2025-07-29 16:13:25.847283000
+0200
@@ -0,0 +1,4 @@
+# Web Environment Vars
+RACK_ENV=production
+VAR="quotation"
+BAR=foo
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycomposefile-0.0.32/sample/my_secret.txt
new/pycomposefile-0.0.34/sample/my_secret.txt
--- old/pycomposefile-0.0.32/sample/my_secret.txt 1970-01-01
01:00:00.000000000 +0100
+++ new/pycomposefile-0.0.34/sample/my_secret.txt 2025-07-29
16:13:25.847283000 +0200
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycomposefile-0.0.32/sample/opt/runtime_opts.env
new/pycomposefile-0.0.34/sample/opt/runtime_opts.env
--- old/pycomposefile-0.0.32/sample/opt/runtime_opts.env 1970-01-01
01:00:00.000000000 +0100
+++ new/pycomposefile-0.0.34/sample/opt/runtime_opts.env 2025-07-29
16:13:25.847283000 +0200
@@ -0,0 +1,5 @@
+#Runtime Opts
+BAR=faz
+STAGE=dev
+BAZ=snafu
+SHOW=false
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycomposefile-0.0.32/sample/test.env
new/pycomposefile-0.0.34/sample/test.env
--- old/pycomposefile-0.0.32/sample/test.env 1970-01-01 01:00:00.000000000
+0100
+++ new/pycomposefile-0.0.34/sample/test.env 2025-07-29 16:13:25.847283000
+0200
@@ -0,0 +1,4 @@
+# Set Rails/Rack environment
+RACK_ENV=development
+VAR="quoted"
+SOME_VAR="value with equals=sign"
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycomposefile-0.0.32/setup.cfg
new/pycomposefile-0.0.34/setup.cfg
--- old/pycomposefile-0.0.32/setup.cfg 2024-10-22 21:45:10.409924300 +0200
+++ new/pycomposefile-0.0.34/setup.cfg 1970-01-01 01:00:00.000000000 +0100
@@ -1,4 +0,0 @@
-[egg_info]
-tag_build =
-tag_date = 0
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycomposefile-0.0.32/setup.py
new/pycomposefile-0.0.34/setup.py
--- old/pycomposefile-0.0.32/setup.py 2024-10-22 21:45:01.000000000 +0200
+++ new/pycomposefile-0.0.34/setup.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,22 +0,0 @@
-import setuptools
-import os
-
-long_desc = open("README.md").read()
-required = ['pyyaml'] # Comma seperated dependent libraries name
-version = os.environ.get("BUILD_TAG", "0.0.1a1").lstrip("v")
-
-setuptools.setup(
- name="pycomposefile",
- version=version,
- author="Steven Murawski",
- author_email="[email protected]",
- license="MIT",
- description="Structured deserialization of Docker Compose files.",
- long_description=long_desc,
- long_description_content_type="text/markdown",
- url="https://github.com/smurawski/pycomposefile",
- packages=setuptools.find_packages(),
- key_words="docker compose",
- install_requires=required,
- python_requires=">=3.6",
-)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/tests/compose_generator/__init__.py
new/pycomposefile-0.0.34/tests/compose_generator/__init__.py
--- old/pycomposefile-0.0.32/tests/compose_generator/__init__.py
2024-10-22 21:45:01.000000000 +0200
+++ new/pycomposefile-0.0.34/tests/compose_generator/__init__.py
2025-07-29 16:13:25.848283000 +0200
@@ -556,7 +556,7 @@
services:
frontend:
image: awesome/webapp
- build:
+ build:
context: ./webapp
"""
return ComposeGenerator.convert_yaml_to_compose_file(compose)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/tests/environment_variable_evaluation/test_environment_variables_braces.py
new/pycomposefile-0.0.34/tests/environment_variable_evaluation/test_environment_variables_braces.py
---
old/pycomposefile-0.0.32/tests/environment_variable_evaluation/test_environment_variables_braces.py
2024-10-22 21:45:01.000000000 +0200
+++
new/pycomposefile-0.0.34/tests/environment_variable_evaluation/test_environment_variables_braces.py
2025-07-29 16:13:25.848283000 +0200
@@ -2,7 +2,7 @@
import os
from ..compose_generator import ComposeGenerator
from pycomposefile.compose_element import EmptyOrUnsetException
-
+import timeout_decorator
class TestBracesNoUnderscoreNoDigitVariableInterpolation(TestCase):
@@ -19,6 +19,21 @@
compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
self.assertEqual(compose_file.services["frontend"].image,
"awesome/bob")
+ def test_uppercase_without_default_when_unset_in_string_value(self):
+ env_var = "DEFAULTUNSET"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image, "awesome/")
+
+ @timeout_decorator.timeout(5)
+ def test_uppercase_with_nested_default_when_unset_in_string_value(self):
+ env_var = "DEFAULTUNSET"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-${CHILDUNSET:-bob}}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image,
"awesome/bob")
+
@mock.patch.dict(os.environ, {"TESTCPUCOUNT": "1.5"})
def test_uppercase_in_decimal_value(self):
braced_env_var = "{TESTCPUCOUNT}"
@@ -48,6 +63,21 @@
compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
self.assertEqual(compose_file.services["frontend"].image,
"awesome/bob")
+ def test_lowercase_without_default_when_unset_in_string_value(self):
+ env_var = "defaultunset"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image, "awesome/")
+
+ @timeout_decorator.timeout(5)
+ def test_lowercase_with_nested_default_when_unset_in_string_value(self):
+ env_var = "defaultunset"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-${childunset:-bob}}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image,
"awesome/bob")
+
@mock.patch.dict(os.environ, {"testcpucount": "1.5"})
def test_lowercase_in_decimal_value(self):
braced_env_var = "{testcpucount}"
@@ -88,3 +118,86 @@
compose_file =
ComposeGenerator.get_compose_with_double_dollar_sign_env_vars()
self.assertEqual(compose_file.services["frontend"].environment["ENVIRONMENT"],
"$ENVIRONMENT")
+
+ @timeout_decorator.timeout(5)
+ def test_uppercase_with_asterisk_as_default(self):
+ env_var = "DEFAULTUNSET"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-*}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image, "awesome/*")
+
+ @timeout_decorator.timeout(5)
+ def test_uppercase_with_plus_as_default(self):
+ env_var = "DEFAULTUNSET"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-+}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image, "awesome/+")
+
+ @timeout_decorator.timeout(5)
+ def test_uppercase_with_question_as_default(self):
+ env_var = "DEFAULTUNSET"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-?}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image, "awesome/?")
+
+ @timeout_decorator.timeout(5)
+ def test_uppercase_with_rightbrace_as_default(self):
+ env_var = "DEFAULTUNSET"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-a}-}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image,
"awesome/a-}")
+
+ @timeout_decorator.timeout(5)
+ def test_lowercase_with_asterisk_as_default(self):
+ env_var = "defaultunset"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-*}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image, "awesome/*")
+
+ @timeout_decorator.timeout(5)
+ def test_lowercase_with_plus_as_default(self):
+ env_var = "defaultunset"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-+}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image, "awesome/+")
+
+ @timeout_decorator.timeout(5)
+ def test_lowercase_with_question_as_default(self):
+ env_var = "defaultunset"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-?}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image, "awesome/?")
+
+ @timeout_decorator.timeout(5)
+ def test_lowercase_with_rightbrace_as_default(self):
+ env_var = "defaultunset"
+ os.unsetenv(env_var)
+ braced_env_with_default_unset = "{" + env_var + ":-}}"
+ compose_file =
ComposeGenerator.get_compose_with_string_value(braced_env_with_default_unset)
+ self.assertEqual(compose_file.services["frontend"].image, "awesome/}")
+
+ @timeout_decorator.timeout(5)
+ def test_uppercase_two_variables_with_default_in_string_value(self):
+ braced_first_env_var = "{HOSTPORT:-8080}"
+ braced_second_env_var = "{CONTAINERPORT:-80}"
+ compose_file =
ComposeGenerator.get_with_two_environment_variables_in_string_value(braced_first_env_var,
braced_second_env_var)
+ self.assertEqual(f"{compose_file.services['frontend'].ports[0]}",
"8080:80/tcp")
+ self.assertEqual(compose_file.services['frontend'].ports[0].published,
"8080")
+ self.assertEqual(compose_file.services['frontend'].ports[0].target,
"80")
+
+ @timeout_decorator.timeout(5)
+ def test_lowercase_two_variables_with_default_in_string_value(self):
+ braced_first_env_var = "{httpport:-8080}"
+ braced_second_env_var = "{containerport:-80}"
+ compose_file =
ComposeGenerator.get_with_two_environment_variables_in_string_value(braced_first_env_var,
braced_second_env_var)
+ self.assertEqual(f"{compose_file.services['frontend'].ports[0]}",
"8080:80/tcp")
+ self.assertEqual(compose_file.services['frontend'].ports[0].published,
"8080")
+ self.assertEqual(compose_file.services['frontend'].ports[0].target,
"80")
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pycomposefile-0.0.32/tests/service/test_service_environment.py
new/pycomposefile-0.0.34/tests/service/test_service_environment.py
--- old/pycomposefile-0.0.32/tests/service/test_service_environment.py
2024-10-22 21:45:01.000000000 +0200
+++ new/pycomposefile-0.0.34/tests/service/test_service_environment.py
2025-07-29 16:13:25.848283000 +0200
@@ -1,6 +1,7 @@
from unittest import TestCase
from pycomposefile import compose_file
+from pycomposefile.compose_element.compose_list_or_map import
ComposeListOrMapElement
from ..compose_generator import ComposeGenerator
@@ -19,3 +20,38 @@
self.assertEqual(compose_file.services["frontend"].environment["RACK_ENV"],
"development")
self.assertEqual(compose_file.services["frontend"].environment["SHOW"], "true")
self.assertIsNone(compose_file.services["frontend"].environment["USER_INPUT"])
+
+ def test_kvp_with_multiple_equals_issue_35(self):
+ """Test for issue #35: KVP strings with multiple '=' should split on
first '=' only"""
+ # Test the exact scenario from the issue
+ element = ComposeListOrMapElement("JAVA_OPTS=foo=bar&&baz=buzz")
+ self.assertEqual(element["JAVA_OPTS"], "foo=bar&&baz=buzz")
+
+ def test_kvp_splitting_edge_cases(self):
+ """Test various edge cases for KVP splitting to prevent regression"""
+ test_cases = [
+ # (input_string, expected_key, expected_value)
+ ("KEY=value", "KEY", "value"),
+ ("KEY=value=extra", "KEY", "value=extra"),
+ ("KEY=a=b=c=d", "KEY", "a=b=c=d"),
+ ("URL=http://example.com:8080/path?param=value", "URL",
"http://example.com:8080/path?param=value"),
+ ("CONFIG=key1=val1,key2=val2", "CONFIG", "key1=val1,key2=val2"),
+ ("COMPLEX=a=b&c=d&e=f", "COMPLEX", "a=b&c=d&e=f"),
+ ("NO_VALUE", "NO_VALUE", None),
+ ]
+
+ for input_string, expected_key, expected_value in test_cases:
+ with self.subTest(input=input_string):
+ element = ComposeListOrMapElement(input_string)
+ self.assertEqual(element[expected_key], expected_value)
+
+ def test_empty_value_handling(self):
+ """Test handling of empty values - this is existing behavior that
should be preserved"""
+ # EMPTY= should result in None (existing behavior)
+ element = ComposeListOrMapElement("EMPTY=")
+ self.assertIsNone(element["EMPTY"])
+
+ # Test the isValueEmpty method directly
+ element_test = ComposeListOrMapElement(None)
+ self.assertTrue(element_test.isValueEmpty("KEY="))
+ self.assertFalse(element_test.isValueEmpty("KEY=value"))