Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-python-dotenv for
openSUSE:Factory checked in at 2025-09-29 16:31:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-dotenv (Old)
and /work/SRC/openSUSE:Factory/.python-python-dotenv.new.11973 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-python-dotenv"
Mon Sep 29 16:31:04 2025 rev:19 rq:1307324 version:1.1.1
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-python-dotenv/python-python-dotenv.changes
2025-08-17 14:50:15.308292674 +0200
+++
/work/SRC/openSUSE:Factory/.python-python-dotenv.new.11973/python-python-dotenv.changes
2025-09-29 16:31:30.320602276 +0200
@@ -1,0 +2,7 @@
+Fri Sep 26 08:20:06 UTC 2025 - John Paul Adrian Glaubitz
<[email protected]>
+
+- Update to 1.1.1
+ * CLI: Ensure `find_dotenv` work reliably on python 3.13
+ * CLI: revert the use of execvpe on Windows
+
+-------------------------------------------------------------------
Old:
----
python-dotenv-1.1.0.tar.gz
New:
----
python-dotenv-1.1.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-python-dotenv.spec ++++++
--- /var/tmp/diff_new_pack.bBeFEj/_old 2025-09-29 16:31:30.976629771 +0200
+++ /var/tmp/diff_new_pack.bBeFEj/_new 2025-09-29 16:31:30.980629939 +0200
@@ -24,7 +24,7 @@
%endif
%{?sle15_python_module_pythons}
Name: python-python-dotenv
-Version: 1.1.0
+Version: 1.1.1
Release: 0
Summary: Python library for .env support
License: BSD-3-Clause
++++++ python-dotenv-1.1.0.tar.gz -> python-dotenv-1.1.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-dotenv-1.1.0/CHANGELOG.md
new/python-dotenv-1.1.1/CHANGELOG.md
--- old/python-dotenv-1.1.0/CHANGELOG.md 2025-03-25 11:53:46.000000000
+0100
+++ new/python-dotenv-1.1.1/CHANGELOG.md 2025-06-24 06:17:34.000000000
+0200
@@ -5,17 +5,27 @@
The format is based on [Keep a
Changelog](https://keepachangelog.com/en/1.0.0/), and this
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.1.1] - 2025-06-24
+
+## Fixed
+
+* CLI: Ensure `find_dotenv` work reliably on python 3.13 by [@theskumar] in
[#563](https://github.com/theskumar/python-dotenv/pull/563)
+* CLI: revert the use of execvpe on Windows by [@wrongontheinternet] in
[#566](https://github.com/theskumar/python-dotenv/pull/566)
+
## [1.1.0] - 2025-03-25
**Feature**
+
- Add support for python 3.13
- Enhance `dotenv run`, switch to `execvpe` for better resource management and
signal handling ([#523]) by [@eekstunt]
**Fixed**
+
- `find_dotenv` and `load_dotenv` now correctly looks up at the current
directory when running in debugger or pdb ([#553] by [@randomseed42])
**Misc**
+
- Drop support for Python 3.8
## [1.0.1] - 2024-01-23
@@ -406,8 +416,11 @@
[@yannham]: https://github.com/yannham
[@zueve]: https://github.com/zueve
[@randomseed42]: https://github.com/zueve
+[@wrongontheinternet]: https://github.com/wrongontheinternet
-[Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.0.1...HEAD
+[Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.1.1...HEAD
+[1.1.1]: https://github.com/theskumar/python-dotenv/compare/v1.1.0...1.1.1
+[1.1.0]: https://github.com/theskumar/python-dotenv/compare/v1.0.1...v1.1.0
[1.0.1]: https://github.com/theskumar/python-dotenv/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/theskumar/python-dotenv/compare/v0.21.0...v1.0.0
[0.21.1]: https://github.com/theskumar/python-dotenv/compare/v0.21.0...v0.21.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-dotenv-1.1.0/setup.cfg
new/python-dotenv-1.1.1/setup.cfg
--- old/python-dotenv-1.1.0/setup.cfg 2025-03-25 11:53:46.000000000 +0100
+++ new/python-dotenv-1.1.1/setup.cfg 2025-06-24 06:17:34.000000000 +0200
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 1.1.0
+current_version = 1.1.1
commit = True
tag = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-dotenv-1.1.0/src/dotenv/cli.py
new/python-dotenv-1.1.1/src/dotenv/cli.py
--- old/python-dotenv-1.1.0/src/dotenv/cli.py 2025-03-25 11:53:46.000000000
+0100
+++ new/python-dotenv-1.1.1/src/dotenv/cli.py 2025-06-24 06:17:34.000000000
+0200
@@ -5,6 +5,9 @@
from contextlib import contextmanager
from typing import Any, Dict, IO, Iterator, List, Optional
+if sys.platform == 'win32':
+ from subprocess import Popen
+
try:
import click
except ImportError:
@@ -187,4 +190,16 @@
cmd_env = os.environ.copy()
cmd_env.update(env)
- os.execvpe(command[0], args=command, env=cmd_env)
+ if sys.platform == 'win32':
+ # execvpe on Windows returns control immediately
+ # rather than once the command has finished.
+ p = Popen(command,
+ universal_newlines=True,
+ bufsize=0,
+ shell=False,
+ env=cmd_env)
+ _, _ = p.communicate()
+
+ exit(p.returncode)
+ else:
+ os.execvpe(command[0], args=command, env=cmd_env)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-dotenv-1.1.0/src/dotenv/main.py
new/python-dotenv-1.1.1/src/dotenv/main.py
--- old/python-dotenv-1.1.0/src/dotenv/main.py 2025-03-25 11:53:46.000000000
+0100
+++ new/python-dotenv-1.1.1/src/dotenv/main.py 2025-06-24 06:17:34.000000000
+0200
@@ -286,6 +286,8 @@
def _is_interactive():
"""Decide whether this is running in a REPL or IPython notebook"""
+ if hasattr(sys, "ps1") or hasattr(sys, "ps2"):
+ return True
try:
main = __import__("__main__", None, None, fromlist=["__file__"])
except ModuleNotFoundError:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-dotenv-1.1.0/src/dotenv/version.py
new/python-dotenv-1.1.1/src/dotenv/version.py
--- old/python-dotenv-1.1.0/src/dotenv/version.py 2025-03-25
11:53:46.000000000 +0100
+++ new/python-dotenv-1.1.1/src/dotenv/version.py 2025-06-24
06:17:34.000000000 +0200
@@ -1 +1 @@
-__version__ = "1.1.0"
+__version__ = "1.1.1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-dotenv-1.1.0/tests/test_is_interactive.py
new/python-dotenv-1.1.1/tests/test_is_interactive.py
--- old/python-dotenv-1.1.0/tests/test_is_interactive.py 1970-01-01
01:00:00.000000000 +0100
+++ new/python-dotenv-1.1.1/tests/test_is_interactive.py 2025-06-24
06:17:34.000000000 +0200
@@ -0,0 +1,227 @@
+import sys
+import builtins
+from unittest import mock
+from dotenv.main import find_dotenv
+
+
+class TestIsInteractive:
+ """Tests for the _is_interactive helper function within find_dotenv.
+
+ The _is_interactive function is used by find_dotenv to determine if the
code
+ is running in an interactive environment (like a REPL, IPython notebook,
etc.)
+ versus a normal script execution.
+
+ Interactive environments include:
+ - Python REPL (has sys.ps1 or sys.ps2)
+ - IPython notebooks (no __file__ in __main__)
+ - Interactive shells
+
+ Non-interactive environments include:
+ - Normal script execution (has __file__ in __main__)
+ - Module imports
+
+ Examples of the behavior:
+ >>> import sys
+ >>> # In a REPL:
+ >>> hasattr(sys, 'ps1') # True
+ >>> # In a script:
+ >>> hasattr(sys, 'ps1') # False
+ """
+
+ def _create_dotenv_file(self, tmp_path):
+ """Helper to create a test .env file."""
+ dotenv_path = tmp_path / ".env"
+ dotenv_path.write_text("TEST=value")
+ return dotenv_path
+
+ def _setup_subdir_and_chdir(self, tmp_path, monkeypatch):
+ """Helper to create subdirectory and change to it."""
+ test_dir = tmp_path / "subdir"
+ test_dir.mkdir()
+ monkeypatch.chdir(test_dir)
+ return test_dir
+
+ def _remove_ps_attributes(self, monkeypatch):
+ """Helper to remove ps1/ps2 attributes if they exist."""
+ if hasattr(sys, "ps1"):
+ monkeypatch.delattr(sys, "ps1")
+ if hasattr(sys, "ps2"):
+ monkeypatch.delattr(sys, "ps2")
+
+ def _mock_main_import(self, monkeypatch, mock_main_module):
+ """Helper to mock __main__ module import."""
+ original_import = builtins.__import__
+
+ def mock_import(name, *args, **kwargs):
+ if name == "__main__":
+ return mock_main_module
+ return original_import(name, *args, **kwargs)
+
+ monkeypatch.setattr(builtins, "__import__", mock_import)
+
+ def _mock_main_import_error(self, monkeypatch):
+ """Helper to mock __main__ module import that raises
ModuleNotFoundError."""
+ original_import = builtins.__import__
+
+ def mock_import(name, *args, **kwargs):
+ if name == "__main__":
+ raise ModuleNotFoundError("No module named '__main__'")
+ return original_import(name, *args, **kwargs)
+
+ monkeypatch.setattr(builtins, "__import__", mock_import)
+
+ def test_is_interactive_with_ps1(self, tmp_path, monkeypatch):
+ """Test that _is_interactive returns True when sys.ps1 exists."""
+ dotenv_path = self._create_dotenv_file(tmp_path)
+
+ # Mock sys.ps1 to simulate interactive shell
+ monkeypatch.setattr(sys, "ps1", ">>> ", raising=False)
+
+ self._setup_subdir_and_chdir(tmp_path, monkeypatch)
+
+ # When _is_interactive() returns True, find_dotenv should search from
cwd
+ result = find_dotenv()
+ assert result == str(dotenv_path)
+
+ def test_is_interactive_with_ps2(self, tmp_path, monkeypatch):
+ """Test that _is_interactive returns True when sys.ps2 exists."""
+ dotenv_path = self._create_dotenv_file(tmp_path)
+
+ # Mock sys.ps2 to simulate multi-line interactive input
+ monkeypatch.setattr(sys, "ps2", "... ", raising=False)
+
+ self._setup_subdir_and_chdir(tmp_path, monkeypatch)
+
+ # When _is_interactive() returns True, find_dotenv should search from
cwd
+ result = find_dotenv()
+ assert result == str(dotenv_path)
+
+ def test_is_interactive_main_module_not_found(self, tmp_path, monkeypatch):
+ """Test that _is_interactive returns False when __main__ module import
fails."""
+ self._remove_ps_attributes(monkeypatch)
+ self._mock_main_import_error(monkeypatch)
+
+ # Change to directory and test
+ monkeypatch.chdir(tmp_path)
+
+ # Since _is_interactive() returns False, find_dotenv should not find
anything
+ # without usecwd=True
+ result = find_dotenv()
+ assert result == ""
+
+ def test_is_interactive_main_without_file(self, tmp_path, monkeypatch):
+ """Test that _is_interactive returns True when __main__ has no
__file__ attribute."""
+ self._remove_ps_attributes(monkeypatch)
+ dotenv_path = self._create_dotenv_file(tmp_path)
+
+ # Mock __main__ module without __file__ attribute
+ mock_main = mock.MagicMock()
+ del mock_main.__file__ # Remove __file__ attribute
+
+ self._mock_main_import(monkeypatch, mock_main)
+ self._setup_subdir_and_chdir(tmp_path, monkeypatch)
+
+ # When _is_interactive() returns True, find_dotenv should search from
cwd
+ result = find_dotenv()
+ assert result == str(dotenv_path)
+
+ def test_is_interactive_main_with_file(self, tmp_path, monkeypatch):
+ """Test that _is_interactive returns False when __main__ has __file__
attribute."""
+ self._remove_ps_attributes(monkeypatch)
+
+ # Mock __main__ module with __file__ attribute
+ mock_main = mock.MagicMock()
+ mock_main.__file__ = "/path/to/script.py"
+
+ self._mock_main_import(monkeypatch, mock_main)
+
+ # Change to directory and test
+ monkeypatch.chdir(tmp_path)
+
+ # Since _is_interactive() returns False, find_dotenv should not find
anything
+ # without usecwd=True
+ result = find_dotenv()
+ assert result == ""
+
+ def test_is_interactive_precedence_ps1_over_main(self, tmp_path,
monkeypatch):
+ """Test that ps1/ps2 attributes take precedence over __main__ module
check."""
+ dotenv_path = self._create_dotenv_file(tmp_path)
+
+ # Set ps1 attribute
+ monkeypatch.setattr(sys, "ps1", ">>> ", raising=False)
+
+ # Mock __main__ module with __file__ attribute (which would normally
return False)
+ mock_main = mock.MagicMock()
+ mock_main.__file__ = "/path/to/script.py"
+
+ self._mock_main_import(monkeypatch, mock_main)
+ self._setup_subdir_and_chdir(tmp_path, monkeypatch)
+
+ # ps1 should take precedence, so _is_interactive() returns True
+ result = find_dotenv()
+ assert result == str(dotenv_path)
+
+ def test_is_interactive_ps1_and_ps2_both_exist(self, tmp_path,
monkeypatch):
+ """Test that _is_interactive returns True when both ps1 and ps2
exist."""
+ dotenv_path = self._create_dotenv_file(tmp_path)
+
+ # Set both ps1 and ps2 attributes
+ monkeypatch.setattr(sys, "ps1", ">>> ", raising=False)
+ monkeypatch.setattr(sys, "ps2", "... ", raising=False)
+
+ self._setup_subdir_and_chdir(tmp_path, monkeypatch)
+
+ # Should return True with either attribute present
+ result = find_dotenv()
+ assert result == str(dotenv_path)
+
+ def test_is_interactive_main_module_with_file_attribute_none(self,
tmp_path, monkeypatch):
+ """Test _is_interactive when __main__ has __file__ attribute set to
None."""
+ self._remove_ps_attributes(monkeypatch)
+
+ # Mock __main__ module with __file__ = None
+ mock_main = mock.MagicMock()
+ mock_main.__file__ = None
+
+ self._mock_main_import(monkeypatch, mock_main)
+
+ # Mock sys.gettrace to ensure debugger detection returns False
+ monkeypatch.setattr("sys.gettrace", lambda: None)
+
+ monkeypatch.chdir(tmp_path)
+
+ # __file__ = None should still be considered non-interactive
+ # and with no debugger, find_dotenv should not search from cwd
+ result = find_dotenv()
+ assert result == ""
+
+ def test_is_interactive_no_ps_attributes_and_normal_execution(self,
tmp_path, monkeypatch):
+ """Test normal script execution scenario where _is_interactive should
return False."""
+ self._remove_ps_attributes(monkeypatch)
+
+ # Don't mock anything - let it use the real __main__ module
+ # which should have a __file__ attribute in normal execution
+
+ # Change to directory and test
+ monkeypatch.chdir(tmp_path)
+
+ # In normal execution, _is_interactive() should return False
+ # so find_dotenv should not find anything without usecwd=True
+ result = find_dotenv()
+ assert result == ""
+
+ def test_is_interactive_with_usecwd_override(self, tmp_path, monkeypatch):
+ """Test that usecwd=True overrides _is_interactive behavior."""
+ self._remove_ps_attributes(monkeypatch)
+ dotenv_path = self._create_dotenv_file(tmp_path)
+
+ # Mock __main__ module with __file__ attribute (non-interactive)
+ mock_main = mock.MagicMock()
+ mock_main.__file__ = "/path/to/script.py"
+
+ self._mock_main_import(monkeypatch, mock_main)
+ self._setup_subdir_and_chdir(tmp_path, monkeypatch)
+
+ # Even though _is_interactive() returns False, usecwd=True should find
the file
+ result = find_dotenv(usecwd=True)
+ assert result == str(dotenv_path)