Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-posthog for openSUSE:Factory 
checked in at 2026-03-17 19:04:32
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-posthog (Old)
 and      /work/SRC/openSUSE:Factory/.python-posthog.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-posthog"

Tue Mar 17 19:04:32 2026 rev:7 rq:1339442 version:7.9.12

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-posthog/python-posthog.changes    
2026-03-09 16:12:09.998972895 +0100
+++ /work/SRC/openSUSE:Factory/.python-posthog.new.8177/python-posthog.changes  
2026-03-17 19:06:14.633013978 +0100
@@ -1,0 +2,11 @@
+Mon Mar 16 21:34:34 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 7.9.12:
+  * chore(flags): expose flag_definition_cache_provider
+  * chore(ci): fix release attribution
+  * fix(ci): attribute release tag to GitHub App
+  * Update release.yml to support commit signing
+  * feat(llma): support prompt versions in prompts sdk
+  * chore(llma): apply prompt SDK review cleanups
+
+-------------------------------------------------------------------

Old:
----
  posthog-7.9.7.tar.gz

New:
----
  posthog-7.9.12.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-posthog.spec ++++++
--- /var/tmp/diff_new_pack.Z6H1DF/_old  2026-03-17 19:06:15.149035363 +0100
+++ /var/tmp/diff_new_pack.Z6H1DF/_new  2026-03-17 19:06:15.153035528 +0100
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-posthog
-Version:        7.9.7
+Version:        7.9.12
 Release:        0
 Summary:        PostHog is developer-friendly, self-hosted product analytics
 License:        MIT

++++++ posthog-7.9.7.tar.gz -> posthog-7.9.12.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/posthog-7.9.7/PKG-INFO new/posthog-7.9.12/PKG-INFO
--- old/posthog-7.9.7/PKG-INFO  2026-03-05 23:09:32.431455100 +0100
+++ new/posthog-7.9.12/PKG-INFO 2026-03-12 10:01:02.150703400 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: posthog
-Version: 7.9.7
+Version: 7.9.12
 Summary: Integrate PostHog into any python application.
 Home-page: https://github.com/posthog/posthog-python
 Author: Posthog
@@ -97,6 +97,8 @@
 
 ## Development
 
+This repo requires all commits to be signed. To configure commit signing, see 
the [PostHog 
handbook](https://posthog.com/handbook/engineering/security#commit-signing).
+
 ### Testing Locally
 
 We recommend using [uv](https://docs.astral.sh/uv/). It's super fast.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/posthog-7.9.7/README.md new/posthog-7.9.12/README.md
--- old/posthog-7.9.7/README.md 2026-03-05 23:09:03.000000000 +0100
+++ new/posthog-7.9.12/README.md        2026-03-12 10:00:31.000000000 +0100
@@ -22,6 +22,8 @@
 
 ## Development
 
+This repo requires all commits to be signed. To configure commit signing, see 
the [PostHog 
handbook](https://posthog.com/handbook/engineering/security#commit-signing).
+
 ### Testing Locally
 
 We recommend using [uv](https://docs.astral.sh/uv/). It's super fast.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/posthog-7.9.7/posthog/__init__.py 
new/posthog-7.9.12/posthog/__init__.py
--- old/posthog-7.9.7/posthog/__init__.py       2026-03-05 23:09:03.000000000 
+0100
+++ new/posthog-7.9.12/posthog/__init__.py      2026-03-12 10:00:31.000000000 
+0100
@@ -253,6 +253,7 @@
 # Whether to enable feature flag polling for local evaluation by default. 
Defaults to True.
 # We recommend setting this to False if you are only using the personalApiKey 
for evaluating remote config payloads via `get_remote_config_payload` and not 
using local evaluation.
 enable_local_evaluation = True  # type: bool
+flag_definition_cache_provider = None  # type: 
Optional[FlagDefinitionCacheProvider]
 
 default_client = None  # type: Optional[Client]
 
@@ -867,6 +868,7 @@
             enable_exception_autocapture=enable_exception_autocapture,
             log_captured_exceptions=log_captured_exceptions,
             enable_local_evaluation=enable_local_evaluation,
+            flag_definition_cache_provider=flag_definition_cache_provider,
             capture_exception_code_variables=capture_exception_code_variables,
             code_variables_mask_patterns=code_variables_mask_patterns,
             code_variables_ignore_patterns=code_variables_ignore_patterns,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/posthog-7.9.7/posthog/ai/prompts.py 
new/posthog-7.9.12/posthog/ai/prompts.py
--- old/posthog-7.9.7/posthog/ai/prompts.py     2026-03-05 23:09:03.000000000 
+0100
+++ new/posthog-7.9.12/posthog/ai/prompts.py    2026-03-12 10:00:31.000000000 
+0100
@@ -19,6 +19,7 @@
 DEFAULT_CACHE_TTL_SECONDS = 300  # 5 minutes
 
 PromptVariables = Dict[str, Union[str, int, float, bool]]
+PromptCacheKey = tuple[str, Optional[int]]
 
 
 class CachedPrompt:
@@ -29,6 +30,22 @@
         self.fetched_at = fetched_at
 
 
+def _cache_key(name: str, version: Optional[int]) -> PromptCacheKey:
+    """Build a cache key for latest or versioned prompt fetches."""
+    return (name, version)
+
+
+def _prompt_reference(
+    name: str, version: Optional[int], *, capitalize: bool = False
+) -> str:
+    """Format a prompt reference for logs and errors."""
+    prefix = "Prompt" if capitalize else "prompt"
+    label = f'{prefix} "{name}"'
+    if version is not None:
+        return f"{label} version {version}"
+    return label
+
+
 def _is_prompt_api_response(data: Any) -> bool:
     """Check if the response is a valid prompt API response."""
     return (
@@ -63,6 +80,9 @@
         # Fetch with caching and fallback
         template = prompts.get('support-system-prompt', fallback='You are a 
helpful assistant.')
 
+        # Fetch a specific published version
+        prompt_v1 = prompts.get('support-system-prompt', version=1)
+
         # Compile with variables
         system_prompt = prompts.compile(template, {
             'company': 'Acme Corp',
@@ -93,7 +113,7 @@
         self._default_cache_ttl_seconds = (
             default_cache_ttl_seconds or DEFAULT_CACHE_TTL_SECONDS
         )
-        self._cache: Dict[str, CachedPrompt] = {}
+        self._cache: Dict[PromptCacheKey, CachedPrompt] = {}
 
         if posthog is not None:
             self._personal_api_key = getattr(posthog, "personal_api_key", 
None) or ""
@@ -112,6 +132,7 @@
         *,
         cache_ttl_seconds: Optional[int] = None,
         fallback: Optional[str] = None,
+        version: Optional[int] = None,
     ) -> str:
         """
         Fetch a prompt by name from the PostHog API.
@@ -126,6 +147,8 @@
             name: The name of the prompt to fetch
             cache_ttl_seconds: Cache TTL in seconds (defaults to instance 
default)
             fallback: Fallback prompt to use if fetch fails and no cache 
available
+            version: Specific prompt version to fetch. If None, fetches the 
latest
+                version
 
         Returns:
             The prompt string
@@ -138,9 +161,10 @@
             if cache_ttl_seconds is not None
             else self._default_cache_ttl_seconds
         )
+        cache_key = _cache_key(name, version)
 
         # Check cache first
-        cached = self._cache.get(name)
+        cached = self._cache.get(cache_key)
         now = time.time()
 
         if cached is not None:
@@ -151,21 +175,22 @@
 
         # Try to fetch from API
         try:
-            prompt = self._fetch_prompt_from_api(name)
+            prompt = self._fetch_prompt_from_api(name, version)
             fetched_at = time.time()
 
             # Update cache
-            self._cache[name] = CachedPrompt(prompt=prompt, 
fetched_at=fetched_at)
+            self._cache[cache_key] = CachedPrompt(prompt=prompt, 
fetched_at=fetched_at)
 
             return prompt
 
         except Exception as error:
+            prompt_reference = _prompt_reference(name, version)
             # Fallback order:
             # 1. Return stale cache (with warning)
             if cached is not None:
                 log.warning(
-                    '[PostHog Prompts] Failed to fetch prompt "%s", using 
stale cache: %s',
-                    name,
+                    "[PostHog Prompts] Failed to fetch %s, using stale cache: 
%s",
+                    prompt_reference,
                     error,
                 )
                 return cached.prompt
@@ -173,8 +198,8 @@
             # 2. Return fallback (with warning)
             if fallback is not None:
                 log.warning(
-                    '[PostHog Prompts] Failed to fetch prompt "%s", using 
fallback: %s',
-                    name,
+                    "[PostHog Prompts] Failed to fetch %s, using fallback: %s",
+                    prompt_reference,
                     error,
                 )
                 return fallback
@@ -207,27 +232,43 @@
 
         return re.sub(r"\{\{([\w.-]+)\}\}", replace_variable, prompt)
 
-    def clear_cache(self, name: Optional[str] = None) -> None:
+    def clear_cache(
+        self, name: Optional[str] = None, *, version: Optional[int] = None
+    ) -> None:
         """
         Clear cached prompts.
 
         Args:
-            name: Specific prompt to clear. If None, clears all cached prompts.
+            name: Specific prompt name to clear. If None, clears all cached 
prompts.
+            version: Specific prompt version to clear. Requires name.
         """
-        if name is not None:
-            self._cache.pop(name, None)
-        else:
+        if version is not None and name is None:
+            raise ValueError("'version' requires 'name' to be provided")
+
+        if name is None:
             self._cache.clear()
+            return
+
+        if version is not None:
+            self._cache.pop(_cache_key(name, version), None)
+            return
+
+        keys_to_clear = [key for key in self._cache if key[0] == name]
+        for key in keys_to_clear:
+            self._cache.pop(key, None)
 
-    def _fetch_prompt_from_api(self, name: str) -> str:
+    def _fetch_prompt_from_api(self, name: str, version: Optional[int] = None) 
-> str:
         """
         Fetch prompt from PostHog API.
 
-        Endpoint: 
{host}/api/environments/@current/llm_prompts/name/{encoded_name}/?token={encoded_project_api_key}
+        Endpoint:
+            {host}/api/environments/@current/llm_prompts/name/{encoded_name}/
+            ?token={encoded_project_api_key}[&version={version}]
         Auth: Bearer {personal_api_key}
 
         Args:
             name: The name of the prompt to fetch
+            version: Specific prompt version to fetch. If None, fetches the 
latest
 
         Returns:
             The prompt string
@@ -247,8 +288,13 @@
             )
 
         encoded_name = urllib.parse.quote(name, safe="")
-        encoded_project_api_key = urllib.parse.quote(self._project_api_key, 
safe="")
-        url = 
f"{self._host}/api/environments/@current/llm_prompts/name/{encoded_name}/?token={encoded_project_api_key}"
+        query_params: Dict[str, Union[str, int]] = {"token": 
self._project_api_key}
+        if version is not None:
+            query_params["version"] = version
+        encoded_query = urllib.parse.urlencode(query_params)
+        url = 
f"{self._host}/api/environments/@current/llm_prompts/name/{encoded_name}/?{encoded_query}"
+        prompt_reference = _prompt_reference(name, version)
+        prompt_label = _prompt_reference(name, version, capitalize=True)
 
         headers = {
             "Authorization": f"Bearer {self._personal_api_key}",
@@ -259,28 +305,28 @@
 
         if not response.ok:
             if response.status_code == 404:
-                raise Exception(f'[PostHog Prompts] Prompt "{name}" not found')
+                raise Exception(f"[PostHog Prompts] {prompt_label} not found")
 
             if response.status_code == 403:
                 raise Exception(
-                    f'[PostHog Prompts] Access denied for prompt "{name}". '
+                    f"[PostHog Prompts] Access denied for {prompt_reference}. "
                     "Check that your personal_api_key has the correct 
permissions and the LLM prompts feature is enabled."
                 )
 
             raise Exception(
-                f'[PostHog Prompts] Failed to fetch prompt "{name}": HTTP 
{response.status_code}'
+                f"[PostHog Prompts] Failed to fetch {prompt_label}: HTTP 
{response.status_code}"
             )
 
         try:
             data = response.json()
         except Exception:
             raise Exception(
-                f'[PostHog Prompts] Invalid response format for prompt 
"{name}"'
+                f"[PostHog Prompts] Invalid response format for {prompt_label}"
             )
 
         if not _is_prompt_api_response(data):
             raise Exception(
-                f'[PostHog Prompts] Invalid response format for prompt 
"{name}"'
+                f"[PostHog Prompts] Invalid response format for {prompt_label}"
             )
 
         return data["prompt"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/posthog-7.9.7/posthog/test/ai/test_prompts.py 
new/posthog-7.9.12/posthog/test/ai/test_prompts.py
--- old/posthog-7.9.7/posthog/test/ai/test_prompts.py   2026-03-05 
23:09:03.000000000 +0100
+++ new/posthog-7.9.12/posthog/test/ai/test_prompts.py  2026-03-12 
10:00:31.000000000 +0100
@@ -1,6 +1,8 @@
 import unittest
 from unittest.mock import MagicMock, patch
 
+from parameterized import parameterized
+
 from posthog.ai.prompts import Prompts
 
 
@@ -73,6 +75,30 @@
         )
 
     @patch("posthog.ai.prompts._get_session")
+    def test_successfully_fetch_a_specific_prompt_version(self, 
mock_get_session):
+        """Should successfully fetch a specific prompt version."""
+        mock_get = mock_get_session.return_value.get
+        versioned_prompt_response = {
+            **self.mock_prompt_response,
+            "prompt": "Prompt version 1",
+            "version": 1,
+        }
+        mock_get.return_value = 
MockResponse(json_data=versioned_prompt_response)
+
+        posthog = self.create_mock_posthog()
+        prompts = Prompts(posthog)
+
+        result = prompts.get("test-prompt", version=1)
+
+        self.assertEqual(result, versioned_prompt_response["prompt"])
+        mock_get.assert_called_once()
+        call_args = mock_get.call_args
+        self.assertEqual(
+            call_args[0][0],
+            
"https://us.posthog.com/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_test_key&version=1";,
+        )
+
+    @patch("posthog.ai.prompts._get_session")
     @patch("posthog.ai.prompts.time.time")
     def test_return_cached_prompt_when_fresh(self, mock_time, 
mock_get_session):
         """Should return cached prompt when fresh (no API call)."""
@@ -97,6 +123,41 @@
         self.assertEqual(mock_get.call_count, 1)  # No additional fetch
 
     @patch("posthog.ai.prompts._get_session")
+    def test_cache_latest_and_versioned_prompts_separately(self, 
mock_get_session):
+        """Should cache latest and historical prompt versions separately."""
+        mock_get = mock_get_session.return_value.get
+        latest_prompt_response = {
+            **self.mock_prompt_response,
+            "prompt": "Latest prompt",
+            "version": 2,
+        }
+        versioned_prompt_response = {
+            **self.mock_prompt_response,
+            "prompt": "Prompt version 1",
+            "version": 1,
+        }
+
+        mock_get.side_effect = [
+            MockResponse(json_data=latest_prompt_response),
+            MockResponse(json_data=versioned_prompt_response),
+        ]
+
+        posthog = self.create_mock_posthog()
+        prompts = Prompts(posthog)
+
+        self.assertEqual(prompts.get("test-prompt"), 
latest_prompt_response["prompt"])
+        self.assertEqual(
+            prompts.get("test-prompt", version=1),
+            versioned_prompt_response["prompt"],
+        )
+        self.assertEqual(prompts.get("test-prompt"), 
latest_prompt_response["prompt"])
+        self.assertEqual(
+            prompts.get("test-prompt", version=1),
+            versioned_prompt_response["prompt"],
+        )
+        self.assertEqual(mock_get.call_count, 2)
+
+    @patch("posthog.ai.prompts._get_session")
     @patch("posthog.ai.prompts.time.time")
     def test_refetch_when_cache_is_stale(self, mock_time, mock_get_session):
         """Should refetch when cache is stale."""
@@ -197,9 +258,21 @@
 
         self.assertIn("Network error", str(context.exception))
 
+    @parameterized.expand(
+        [
+            ("latest", {}, 'Prompt "nonexistent-prompt" not found'),
+            (
+                "versioned",
+                {"version": 3},
+                'Prompt "nonexistent-prompt" version 3 not found',
+            ),
+        ]
+    )
     @patch("posthog.ai.prompts._get_session")
-    def test_handle_404_response(self, mock_get_session):
-        """Should handle 404 response."""
+    def test_handle_404_response(
+        self, _scenario, get_kwargs, expected_message, mock_get_session
+    ):
+        """Should handle 404 responses for latest and versioned prompts."""
         mock_get = mock_get_session.return_value.get
         mock_get.return_value = MockResponse(status_code=404, ok=False)
 
@@ -207,9 +280,9 @@
         prompts = Prompts(posthog)
 
         with self.assertRaises(Exception) as context:
-            prompts.get("nonexistent-prompt")
+            prompts.get("nonexistent-prompt", **get_kwargs)
 
-        self.assertIn('Prompt "nonexistent-prompt" not found', 
str(context.exception))
+        self.assertIn(expected_message, str(context.exception))
 
     @patch("posthog.ai.prompts._get_session")
     def test_handle_403_response(self, mock_get_session):
@@ -542,6 +615,38 @@
 class TestPromptsClearCache(TestPrompts):
     """Tests for the Prompts.clear_cache() method."""
 
+    def _populate_versioned_cache(self, prompts, mock_get):
+        """Populate cache with latest and versioned entries for the same 
prompt."""
+        latest_prompt_response = {
+            **self.mock_prompt_response,
+            "prompt": "Latest prompt",
+            "version": 2,
+        }
+        versioned_prompt_response = {
+            **self.mock_prompt_response,
+            "prompt": "Prompt version 1",
+            "version": 1,
+        }
+        mock_get.side_effect = [
+            MockResponse(json_data=latest_prompt_response),
+            MockResponse(json_data=versioned_prompt_response),
+        ]
+
+        prompts.get("test-prompt")
+        prompts.get("test-prompt", version=1)
+
+        return latest_prompt_response, versioned_prompt_response
+
+    def test_clear_cache_with_version_and_no_name_raises_value_error(self):
+        """Should enforce that versioned cache clearing requires a prompt 
name."""
+        posthog = self.create_mock_posthog()
+        prompts = Prompts(posthog)
+
+        with self.assertRaises(ValueError) as context:
+            prompts.clear_cache(version=1)
+
+        self.assertIn("requires 'name'", str(context.exception))
+
     @patch("posthog.ai.prompts._get_session")
     def test_clear_a_specific_prompt_from_cache(self, mock_get_session):
         """Should clear a specific prompt from cache."""
@@ -574,6 +679,49 @@
         self.assertEqual(mock_get.call_count, 3)
 
     @patch("posthog.ai.prompts._get_session")
+    def test_clear_a_specific_prompt_version_from_cache(self, 
mock_get_session):
+        """Should clear only the requested prompt version from cache."""
+        mock_get = mock_get_session.return_value.get
+
+        posthog = self.create_mock_posthog()
+        prompts = Prompts(posthog)
+
+        _, versioned_prompt_response = self._populate_versioned_cache(prompts, 
mock_get)
+        self.assertEqual(mock_get.call_count, 2)
+
+        mock_get.side_effect = 
[MockResponse(json_data=versioned_prompt_response)]
+        prompts.clear_cache("test-prompt", version=1)
+
+        prompts.get("test-prompt")
+        self.assertEqual(mock_get.call_count, 2)
+
+        prompts.get("test-prompt", version=1)
+        self.assertEqual(mock_get.call_count, 3)
+
+    @patch("posthog.ai.prompts._get_session")
+    def test_clear_a_prompt_name_clears_all_cached_versions(self, 
mock_get_session):
+        """Should clear latest and versioned cache entries for the same prompt 
name."""
+        mock_get = mock_get_session.return_value.get
+
+        posthog = self.create_mock_posthog()
+        prompts = Prompts(posthog)
+
+        latest_prompt_response, versioned_prompt_response = (
+            self._populate_versioned_cache(prompts, mock_get)
+        )
+        self.assertEqual(mock_get.call_count, 2)
+
+        mock_get.side_effect = [
+            MockResponse(json_data=latest_prompt_response),
+            MockResponse(json_data=versioned_prompt_response),
+        ]
+        prompts.clear_cache("test-prompt")
+
+        prompts.get("test-prompt")
+        prompts.get("test-prompt", version=1)
+        self.assertEqual(mock_get.call_count, 4)
+
+    @patch("posthog.ai.prompts._get_session")
     def test_clear_all_prompts_from_cache(self, mock_get_session):
         """Should clear all prompts from cache when no name is provided."""
         mock_get = mock_get_session.return_value.get
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/posthog-7.9.7/posthog/version.py 
new/posthog-7.9.12/posthog/version.py
--- old/posthog-7.9.7/posthog/version.py        2026-03-05 23:09:29.000000000 
+0100
+++ new/posthog-7.9.12/posthog/version.py       2026-03-12 10:00:55.000000000 
+0100
@@ -1 +1 @@
-VERSION = "7.9.7"
+VERSION = "7.9.12"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/posthog-7.9.7/posthog.egg-info/PKG-INFO 
new/posthog-7.9.12/posthog.egg-info/PKG-INFO
--- old/posthog-7.9.7/posthog.egg-info/PKG-INFO 2026-03-05 23:09:32.000000000 
+0100
+++ new/posthog-7.9.12/posthog.egg-info/PKG-INFO        2026-03-12 
10:01:02.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: posthog
-Version: 7.9.7
+Version: 7.9.12
 Summary: Integrate PostHog into any python application.
 Home-page: https://github.com/posthog/posthog-python
 Author: Posthog
@@ -97,6 +97,8 @@
 
 ## Development
 
+This repo requires all commits to be signed. To configure commit signing, see 
the [PostHog 
handbook](https://posthog.com/handbook/engineering/security#commit-signing).
+
 ### Testing Locally
 
 We recommend using [uv](https://docs.astral.sh/uv/). It's super fast.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/posthog-7.9.7/pyproject.toml 
new/posthog-7.9.12/pyproject.toml
--- old/posthog-7.9.7/pyproject.toml    2026-03-05 23:09:28.000000000 +0100
+++ new/posthog-7.9.12/pyproject.toml   2026-03-12 10:00:55.000000000 +0100
@@ -4,7 +4,7 @@
 
 [project]
 name = "posthog"
-version = "7.9.7"
+version = "7.9.12"
 description = "Integrate PostHog into any python application."
 authors = [{ name = "PostHog", email = "[email protected]" }]
 maintainers = [{ name = "PostHog", email = "[email protected]" }]

Reply via email to