asanypath-native 2.1.0__tar.gz → 2.1.1__tar.gz

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.
@@ -96,7 +96,7 @@ dependencies = [
96
96
 
97
97
  [[package]]
98
98
  name = "asanypath-native"
99
- version = "2.1.0"
99
+ version = "2.1.1"
100
100
  dependencies = [
101
101
  "anyhow",
102
102
  "aws-config",
@@ -311,9 +311,9 @@ dependencies = [
311
311
 
312
312
  [[package]]
313
313
  name = "aws-sigv4"
314
- version = "1.5.3"
314
+ version = "1.6.0"
315
315
  source = "registry+https://github.com/rust-lang/crates.io-index"
316
- checksum = "31d955e76ff96acd555bf06fa0fa6d5bf9335fa84ae7c64481b20ae61d231f70"
316
+ checksum = "2312577f088c9fbf4206dfdb884cf1de9407b43e1a923cbed5237775116fc24b"
317
317
  dependencies = [
318
318
  "aws-credential-types",
319
319
  "aws-smithy-http",
@@ -448,15 +448,14 @@ dependencies = [
448
448
 
449
449
  [[package]]
450
450
  name = "aws-smithy-runtime-api"
451
- version = "1.16.2"
451
+ version = "1.19.0"
452
452
  source = "registry+https://github.com/rust-lang/crates.io-index"
453
- checksum = "6d881a7b7ad179fd6611680c9de89f716fb00ab40299a9a7b8c6913e8f7511a8"
453
+ checksum = "c0730c16f91124c6a2abb4932c77e299288b3dd9f967ea2e9ec48cc6731e87a4"
454
454
  dependencies = [
455
455
  "aws-smithy-async",
456
456
  "aws-smithy-runtime-api-macros",
457
457
  "aws-smithy-types",
458
458
  "bytes",
459
- "http 0.2.12",
460
459
  "http 1.5.0",
461
460
  "pin-project-lite",
462
461
  "tokio",
@@ -488,9 +487,9 @@ dependencies = [
488
487
 
489
488
  [[package]]
490
489
  name = "aws-smithy-types"
491
- version = "1.6.4"
490
+ version = "1.8.1"
492
491
  source = "registry+https://github.com/rust-lang/crates.io-index"
493
- checksum = "0b791f3ac597193fe1d08b82366986eb1f5bc31f2ac6c194c0855276116c76cd"
492
+ checksum = "69bb407740a197147da48238ecc94498493c9e85445732360cec180296ca45f1"
494
493
  dependencies = [
495
494
  "base64-simd",
496
495
  "bytes",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "asanypath-native"
3
- version = "2.1.0"
3
+ version = "2.1.1"
4
4
  edition = "2024"
5
5
  license = "MIT"
6
6
  publish = false
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asanypath-native
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: Native cloud acceleration for asanypath (Rust/tokio/reqwest)
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.10
@@ -0,0 +1,125 @@
1
+ # Copyright (C) 2026 Axis Communications AB, Lund, Sweden
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ """Type stub for the Rust-native asanypath_native extension module."""
8
+
9
+ from typing import Any
10
+
11
+ # -- S3 --
12
+ async def s3_get_batch(*args: Any, **kwargs: Any) -> Any: ...
13
+ async def s3_put_batch(*args: Any, **kwargs: Any) -> Any: ...
14
+ async def s3_copy_batch(*args: Any, **kwargs: Any) -> Any: ...
15
+ async def s3_delete_batch(*args: Any, **kwargs: Any) -> Any: ...
16
+ async def s3_exists_batch(*args: Any, **kwargs: Any) -> Any: ...
17
+ async def s3_list_batch(*args: Any, **kwargs: Any) -> Any: ...
18
+ async def s3_head_batch(*args: Any, **kwargs: Any) -> Any: ...
19
+ async def s3_is_dir(*args: Any, **kwargs: Any) -> Any: ...
20
+ async def s3_is_dir_batch(*args: Any, **kwargs: Any) -> Any: ...
21
+ async def s3_list_buckets(*args: Any, **kwargs: Any) -> Any: ...
22
+ async def s3_get_acl(*args: Any, **kwargs: Any) -> Any: ...
23
+ async def s3_put_acl(*args: Any, **kwargs: Any) -> Any: ...
24
+ async def s3_get_credentials(
25
+ region_override: str | None,
26
+ access_key: str | None,
27
+ secret_key: str | None,
28
+ session_token: str | None,
29
+ ) -> tuple[str, str, str | None, str, str | None]: ...
30
+ def s3_presign(
31
+ endpoint: str,
32
+ bucket: str,
33
+ key: str,
34
+ region: str,
35
+ access_key: str,
36
+ secret_key: str,
37
+ session_token: str | None = None,
38
+ method: str | None = None,
39
+ expires: int | None = None,
40
+ ) -> str: ...
41
+
42
+ # -- GCS --
43
+ async def gcs_get_batch(*args: Any, **kwargs: Any) -> Any: ...
44
+ async def gcs_put_batch(*args: Any, **kwargs: Any) -> Any: ...
45
+ async def gcs_copy_batch(*args: Any, **kwargs: Any) -> Any: ...
46
+ async def gcs_delete_batch(*args: Any, **kwargs: Any) -> Any: ...
47
+ async def gcs_exists_batch(*args: Any, **kwargs: Any) -> Any: ...
48
+ async def gcs_list_batch(*args: Any, **kwargs: Any) -> Any: ...
49
+ async def gcs_head(*args: Any, **kwargs: Any) -> Any: ...
50
+ async def gcs_head_batch(*args: Any, **kwargs: Any) -> Any: ...
51
+ async def gcs_is_dir(*args: Any, **kwargs: Any) -> Any: ...
52
+ async def gcs_is_dir_batch(*args: Any, **kwargs: Any) -> Any: ...
53
+ async def gcs_list_buckets(*args: Any, **kwargs: Any) -> Any: ...
54
+ def gcs_presign(
55
+ bucket: str,
56
+ object_path: str,
57
+ service_account_json: str,
58
+ method: str | None = None,
59
+ expires: int | None = None,
60
+ ) -> str: ...
61
+
62
+ # -- Azure --
63
+ async def az_get_batch(*args: Any, **kwargs: Any) -> Any: ...
64
+ async def az_put_batch(*args: Any, **kwargs: Any) -> Any: ...
65
+ async def az_copy_batch(*args: Any, **kwargs: Any) -> Any: ...
66
+ async def az_delete_batch(*args: Any, **kwargs: Any) -> Any: ...
67
+ async def az_exists_batch(*args: Any, **kwargs: Any) -> Any: ...
68
+ async def az_list_batch(*args: Any, **kwargs: Any) -> Any: ...
69
+ async def az_head_batch(*args: Any, **kwargs: Any) -> Any: ...
70
+ async def az_is_dir(*args: Any, **kwargs: Any) -> Any: ...
71
+ async def az_is_dir_batch(*args: Any, **kwargs: Any) -> Any: ...
72
+ async def az_list_containers(*args: Any, **kwargs: Any) -> Any: ...
73
+ def az_presign(
74
+ endpoint: str,
75
+ container: str,
76
+ blob_path: str,
77
+ account_name: str,
78
+ account_key: str | None = None,
79
+ sas_token: str | None = None,
80
+ method: str | None = None,
81
+ expires: int | None = None,
82
+ ) -> str: ...
83
+
84
+ # -- Artifactory --
85
+ async def art_get_batch(*args: Any, **kwargs: Any) -> Any: ...
86
+ async def art_put_batch(*args: Any, **kwargs: Any) -> Any: ...
87
+ async def art_copy_batch(*args: Any, **kwargs: Any) -> Any: ...
88
+ async def art_delete_batch(*args: Any, **kwargs: Any) -> Any: ...
89
+ async def art_exists_batch(*args: Any, **kwargs: Any) -> Any: ...
90
+ async def art_list_batch(*args: Any, **kwargs: Any) -> Any: ...
91
+ async def art_list_repos(*args: Any, **kwargs: Any) -> Any: ...
92
+ async def art_storage_info(*args: Any, **kwargs: Any) -> Any: ...
93
+
94
+ # -- HTTP --
95
+ async def http_get(*args: Any, **kwargs: Any) -> Any: ...
96
+ async def http_put(*args: Any, **kwargs: Any) -> Any: ...
97
+ async def http_post(*args: Any, **kwargs: Any) -> Any: ...
98
+ async def http_delete(*args: Any, **kwargs: Any) -> Any: ...
99
+ async def http_head(*args: Any, **kwargs: Any) -> Any: ...
100
+ async def http_patch(*args: Any, **kwargs: Any) -> Any: ...
101
+ async def http_options(*args: Any, **kwargs: Any) -> Any: ...
102
+ async def http_exists(*args: Any, **kwargs: Any) -> Any: ...
103
+ async def http_scrape_links(*args: Any, **kwargs: Any) -> Any: ...
104
+ async def http_request(*args: Any, **kwargs: Any) -> Any: ...
105
+
106
+ # -- Unified --
107
+ async def range_read(path: str, start: int, end: int, **kwargs: Any) -> bytes: ...
108
+
109
+ # -- SSH / SFTP --
110
+ async def ssh_read(*args: Any, **kwargs: Any) -> bytes: ...
111
+ async def ssh_read_range(*args: Any, **kwargs: Any) -> bytes: ...
112
+ async def ssh_write(*args: Any, **kwargs: Any) -> int: ...
113
+ async def ssh_exists(*args: Any, **kwargs: Any) -> bool: ...
114
+ async def ssh_stat(*args: Any, **kwargs: Any) -> Any: ...
115
+ async def ssh_lstat(*args: Any, **kwargs: Any) -> Any: ...
116
+ async def ssh_setstat(*args: Any, **kwargs: Any) -> None: ...
117
+ async def ssh_list(*args: Any, **kwargs: Any) -> Any: ...
118
+ async def ssh_mkdir(*args: Any, **kwargs: Any) -> None: ...
119
+ async def ssh_rmdir(*args: Any, **kwargs: Any) -> None: ...
120
+ async def ssh_unlink(*args: Any, **kwargs: Any) -> None: ...
121
+ async def ssh_rename(*args: Any, **kwargs: Any) -> None: ...
122
+ def ssh_resolve_config(
123
+ host: str, config_paths: list[str]
124
+ ) -> tuple[str | None, int | None, str | None, str | None]: ...
125
+ def ssh_disconnect_all() -> None: ...
@@ -11,6 +11,9 @@ use std::collections::HashMap;
11
11
  use std::sync::OnceLock;
12
12
  use std::time::SystemTime;
13
13
 
14
+ use aws_config::environment::region::EnvironmentVariableRegionProvider;
15
+ use aws_config::meta::region::RegionProviderChain;
16
+ use aws_config::profile::region::ProfileFileRegionProvider;
14
17
  use aws_config::{BehaviorVersion, Region};
15
18
  use aws_credential_types::Credentials;
16
19
  use aws_credential_types::provider::{ProvideCredentials, SharedCredentialsProvider};
@@ -66,20 +69,54 @@ async fn s3_aws_config(region_override: Option<String>) -> Result<S3AwsConfig, S
66
69
  return Ok(config.clone());
67
70
  }
68
71
 
69
- let mut loader = aws_config::defaults(BehaviorVersion::latest());
70
- if let Some(profile) = profile {
72
+ // Resolve region WITHOUT IMDS: explicit override > AWS_REGION env > profile.
73
+ // The loader must then receive a *concrete* region: an unset region makes the
74
+ // credentials chain probe EC2 IMDS during load() (a ~3s timeout off-EC2), which
75
+ // is pointless when a custom endpoint_url is configured (S3-compatible stores).
76
+ let region_no_imds: Option<Region> = match &region_override {
77
+ Some(region) => Some(Region::new(region.clone())),
78
+ None => {
79
+ RegionProviderChain::first_try(EnvironmentVariableRegionProvider::new())
80
+ .or_else(ProfileFileRegionProvider::default())
81
+ .region()
82
+ .await
83
+ }
84
+ };
85
+
86
+ // First load with a concrete placeholder region (no IMDS) to read endpoint_url.
87
+ let probe_region = region_no_imds
88
+ .clone()
89
+ .unwrap_or_else(|| Region::new("us-east-1"));
90
+ let mut loader = aws_config::defaults(BehaviorVersion::latest()).region(probe_region);
91
+ if let Some(profile) = &profile {
71
92
  loader = loader.profile_name(profile);
72
93
  }
73
- if let Some(region) = region_override {
74
- loader = loader.region(Region::new(region));
75
- }
76
94
  let sdk_config = loader.load().await;
95
+ let endpoint_url = sdk_config.endpoint_url().map(str::to_string);
96
+
97
+ // Preserve IMDS: only a genuine real-AWS target (no endpoint, no configured
98
+ // region) consults the IMDS region provider; then reload so the credentials
99
+ // chain is built against the IMDS-resolved region.
100
+ let (region, sdk_config) = if let Some(region) = region_no_imds {
101
+ (region.as_ref().to_string(), sdk_config)
102
+ } else if endpoint_url.is_some() {
103
+ ("us-east-1".to_string(), sdk_config)
104
+ } else {
105
+ match RegionProviderChain::default_provider().region().await {
106
+ Some(imds_region) => {
107
+ let mut loader =
108
+ aws_config::defaults(BehaviorVersion::latest()).region(imds_region.clone());
109
+ if let Some(profile) = &profile {
110
+ loader = loader.profile_name(profile);
111
+ }
112
+ (imds_region.as_ref().to_string(), loader.load().await)
113
+ }
114
+ None => ("us-east-1".to_string(), sdk_config),
115
+ }
116
+ };
77
117
  let config = S3AwsConfig {
78
- region: sdk_config
79
- .region()
80
- .map(|region| region.as_ref().to_string())
81
- .unwrap_or_else(|| "us-east-1".to_string()),
82
- endpoint_url: sdk_config.endpoint_url().map(str::to_string),
118
+ region,
119
+ endpoint_url,
83
120
  credentials_provider: sdk_config
84
121
  .credentials_provider()
85
122
  .ok_or_else(|| "AWS credential provider chain is unavailable".to_string())?,