fleet-python 0.2.69b2__py3-none-any.whl → 0.2.69b3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fleet-python might be problematic. Click here for more details.

fleet/client.py CHANGED
@@ -22,6 +22,7 @@ import json
22
22
  import logging
23
23
  import os
24
24
  from typing import List, Optional, Dict, Any, TYPE_CHECKING, Union
25
+ from urllib.parse import urlparse
25
26
 
26
27
  from .base import EnvironmentBase, SyncWrapper
27
28
  from .models import (
@@ -74,6 +75,14 @@ class SyncEnv(EnvironmentBase):
74
75
  self._client = client
75
76
  self._apps: Dict[str, InstanceClient] = {}
76
77
  self._instance: Optional[InstanceClient] = None
78
+ self._manager_url_override: Optional[str] = None # For URL mode
79
+
80
+ @property
81
+ def manager_url(self) -> str:
82
+ """Override to support URL mode where urls is None."""
83
+ if self._manager_url_override is not None:
84
+ return self._manager_url_override
85
+ return super().manager_url
77
86
 
78
87
  @property
79
88
  def instance(self) -> InstanceClient:
@@ -85,17 +94,17 @@ class SyncEnv(EnvironmentBase):
85
94
 
86
95
  def app(self, name: str) -> InstanceClient:
87
96
  if name not in self._apps:
88
- # Extract base URL by removing the current app path (e.g., /sentry/api/v1/env)
89
- # manager_url looks like: https://xxx.fleetai.com/sentry/api/v1/env
90
- base_url = self.manager_url.split("/api/v1/env")[0]
91
- # Remove the current app name (e.g., /sentry) to get the root
92
- if "/" in base_url:
93
- parts = base_url.rsplit("/", 1)
94
- if len(parts) == 2 and parts[0] != "https:/":
95
- base_url = parts[0]
97
+ # Extract scheme://netloc from manager_url, then construct /{name}/api/v1/env
98
+ # Supports all URL formats:
99
+ # https://host/api/v1/env -> https://host/{name}/api/v1/env
100
+ # https://host/sentry/api/v1/env -> https://host/{name}/api/v1/env
101
+ # http://localhost:8080/api/v1/env -> http://localhost:8080/{name}/api/v1/env
102
+ parsed = urlparse(self.manager_url)
103
+ root = f"{parsed.scheme}://{parsed.netloc}"
104
+ new_url = f"{root}/{name}/api/v1/env"
96
105
 
97
106
  self._apps[name] = InstanceClient(
98
- f"{base_url}/{name}/api/v1/env",
107
+ new_url,
99
108
  self._client.httpx_client if self._client else None,
100
109
  )
101
110
  return self._apps[name]
@@ -353,6 +362,7 @@ class Fleet:
353
362
  health=None,
354
363
  )
355
364
  env._instance = instance_client
365
+ env._manager_url_override = base_url # Set manager_url for URL mode
356
366
  return env
357
367
 
358
368
  @staticmethod
@@ -448,6 +458,7 @@ class Fleet:
448
458
  health=None,
449
459
  )
450
460
  env._instance = instance_client
461
+ env._manager_url_override = "local://" # Set manager_url for local mode
451
462
  return env
452
463
 
453
464
  def check_bundle_exists(self, bundle_hash: str) -> VerifiersCheckResponse:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleet-python
3
- Version: 0.2.69b2
3
+ Version: 0.2.69b3
4
4
  Summary: Python SDK for Fleet environments
5
5
  Author-email: Fleet AI <nic@fleet.so>
6
6
  License: Apache-2.0
@@ -23,7 +23,7 @@ examples/quickstart.py,sha256=1VT39IRRhemsJgxi0O0gprdpcw7HB4pYO97GAYagIcg,3788
23
23
  examples/test_cdp_logging.py,sha256=AkCwQCgOTQEI8w3v0knWK_4eXMph7L9x07wj9yIYM10,2836
24
24
  fleet/__init__.py,sha256=yC4HIcbtPAPOgI0lLri3l8nbXkNee9JOihKAc7SXYkY,4201
25
25
  fleet/base.py,sha256=bc-340sTpq_DJs7yQ9d2pDWnmJFmA1SwDB9Lagvqtb4,9182
26
- fleet/client.py,sha256=HPBKsGeWuLoBXFmEdZuLz04aYDKmg0_eYSgQ2-O4tNg,39521
26
+ fleet/client.py,sha256=lNVbAzq23p6-x66YduSdm4ZZC8yJreM82QVIKvDknuQ,40046
27
27
  fleet/config.py,sha256=uY02ZKxVoXqVDta-0IMWaYJeE1CTXF_fA9NI6QUutmU,319
28
28
  fleet/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
29
29
  fleet/global_client.py,sha256=frrDAFNM2ywN0JHLtlm9qbE1dQpnQJsavJpb7xSR_bU,1072
@@ -69,15 +69,16 @@ fleet/verifiers/decorator.py,sha256=nAP3O8szXu7md_kpwpz91hGSUNEVLYjwZQZTkQlV1DM,
69
69
  fleet/verifiers/parse.py,sha256=qz9AfJrTbjlg-LU-lE8Ciqi7Yt2a8-cs17FdpjTLhMk,8550
70
70
  fleet/verifiers/sql_differ.py,sha256=TqTLWyK3uOyLbitT6HYzYEzuSFC39wcyhgk3rcm__k8,6525
71
71
  fleet/verifiers/verifier.py,sha256=_lcxXVm8e0xRrK2gNJy9up7pW1zOkPRY5n5lQ85S8jg,14197
72
- fleet_python-0.2.69b2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
72
+ fleet_python-0.2.69b3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
73
73
  scripts/fix_sync_imports.py,sha256=X9fWLTpiPGkSHsjyQUDepOJkxOqw1DPj7nd8wFlFqLQ,8368
74
74
  scripts/unasync.py,sha256=vWVQxRWX8SRZO5cmzEhpvnG_REhCWXpidIGIpWmEcvI,696
75
75
  tests/__init__.py,sha256=Re1SdyxH8NfyL1kjhi7SQkGP1mYeWB-D6UALqdIMd8I,35
76
+ tests/test_app_method.py,sha256=kg2IiL75cH-HmsTMS3_wDL39dAesgfv_AT6jVthd5J4,3159
76
77
  tests/test_instance_dispatch.py,sha256=CvU4C3LBIqsYZdEsEFfontGjyxAZfVYyXnGwxyIvXOc,23065
77
78
  tests/test_sqlite_resource_dual_mode.py,sha256=Mh8jBd-xsIGDYFsOACKKK_5DXMUYlFFS7W-jaY6AjG4,8734
78
79
  tests/test_sqlite_shared_memory_behavior.py,sha256=fKx_1BmLS3b8x-9pMgjMycpnaHWY8P-2ZuXEspx6Sbw,4082
79
80
  tests/test_verifier_from_string.py,sha256=Lxi3TpFHFb-hG4-UhLKZJkqo84ax9YJY8G6beO-1erM,13581
80
- fleet_python-0.2.69b2.dist-info/METADATA,sha256=uUc9gJCTRFAIXetYKsETKq-P62QdGF35_wjxDjSYMcw,3306
81
- fleet_python-0.2.69b2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
82
- fleet_python-0.2.69b2.dist-info/top_level.txt,sha256=qb1zIbtEktyhRFZdqVytwg54l64qtoZL0wjHB4bUg3c,29
83
- fleet_python-0.2.69b2.dist-info/RECORD,,
81
+ fleet_python-0.2.69b3.dist-info/METADATA,sha256=M4OF4x_-pFl_h9xAh7ERomXkssTvlrH5HxDPq-C75Ng,3306
82
+ fleet_python-0.2.69b3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
83
+ fleet_python-0.2.69b3.dist-info/top_level.txt,sha256=qb1zIbtEktyhRFZdqVytwg54l64qtoZL0wjHB4bUg3c,29
84
+ fleet_python-0.2.69b3.dist-info/RECORD,,
@@ -0,0 +1,85 @@
1
+ """Unit tests for SyncEnv.app() method URL handling."""
2
+
3
+ import pytest
4
+ from unittest.mock import Mock, patch
5
+ from fleet.client import Fleet
6
+
7
+
8
+ class TestAppMethod:
9
+ """Test SyncEnv.app() method with different URL formats."""
10
+
11
+ @pytest.fixture
12
+ def fleet_client(self):
13
+ """Create a Fleet client with mocked HTTP client."""
14
+ with patch("fleet.client.default_httpx_client") as mock_client:
15
+ mock_client.return_value = Mock()
16
+ client = Fleet(api_key="test_key")
17
+ client.client.request = Mock()
18
+ return client
19
+
20
+ def test_app_with_existing_app_path(self, fleet_client):
21
+ """Test app() with URL that already has an app path like /sentry."""
22
+ # Create instance with a URL that has an existing app path
23
+ env = fleet_client.instance("https://example.com/sentry/api/v1/env")
24
+
25
+ # Access jira app
26
+ jira_client = env.app("jira")
27
+
28
+ # Check the constructed URL
29
+ assert jira_client.base_url == "https://example.com/jira/api/v1/env", \
30
+ f"Expected https://example.com/jira/api/v1/env, got {jira_client.base_url}"
31
+
32
+ def test_app_without_app_path(self, fleet_client):
33
+ """Test app() with URL that has no app path (just /api/v1/env)."""
34
+ # Create instance with a URL without an app path
35
+ env = fleet_client.instance("https://example.com/api/v1/env")
36
+
37
+ # Access jira app
38
+ jira_client = env.app("jira")
39
+
40
+ # Check the constructed URL
41
+ assert jira_client.base_url == "https://example.com/jira/api/v1/env", \
42
+ f"Expected https://example.com/jira/api/v1/env, got {jira_client.base_url}"
43
+
44
+ def test_app_with_different_app_names(self, fleet_client):
45
+ """Test app() with multiple different app names."""
46
+ env = fleet_client.instance("https://example.com/api/v1/env")
47
+
48
+ jira = env.app("jira")
49
+ sentry = env.app("sentry")
50
+ github = env.app("github")
51
+
52
+ assert jira.base_url == "https://example.com/jira/api/v1/env"
53
+ assert sentry.base_url == "https://example.com/sentry/api/v1/env"
54
+ assert github.base_url == "https://example.com/github/api/v1/env"
55
+
56
+ def test_app_caching(self, fleet_client):
57
+ """Test that app() caches InstanceClient instances."""
58
+ env = fleet_client.instance("https://example.com/api/v1/env")
59
+
60
+ # Call app("jira") twice
61
+ jira1 = env.app("jira")
62
+ jira2 = env.app("jira")
63
+
64
+ # Should return the same cached instance
65
+ assert jira1 is jira2
66
+
67
+ def test_app_with_localhost(self, fleet_client):
68
+ """Test app() with localhost URLs."""
69
+ env = fleet_client.instance("http://localhost:8080/api/v1/env")
70
+
71
+ jira = env.app("jira")
72
+
73
+ assert jira.base_url == "http://localhost:8080/jira/api/v1/env"
74
+
75
+ def test_app_with_port(self, fleet_client):
76
+ """Test app() with URLs that include port numbers."""
77
+ env = fleet_client.instance("https://example.com:9000/api/v1/env")
78
+
79
+ jira = env.app("jira")
80
+
81
+ assert jira.base_url == "https://example.com:9000/jira/api/v1/env"
82
+
83
+
84
+ if __name__ == "__main__":
85
+ pytest.main([__file__, "-v"])