This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch xuanwo/drop-python-scheme
in repository https://gitbox.apache.org/repos/asf/opendal.git

commit b53772e365c9d591c6f5d886b448ea2f05e17122
Author: Xuanwo <[email protected]>
AuthorDate: Thu Dec 4 18:14:09 2025 +0800

    refactor: Remove schema from python binding
    
    Signed-off-by: Xuanwo <[email protected]>
---
 bindings/python/python/opendal/operator.pyi |  14 ++
 bindings/python/src/operator.rs             |  58 ++---
 bindings/python/src/services.rs             | 376 ++++++++++++----------------
 dev/src/generate/python.j2                  |  81 +++---
 4 files changed, 245 insertions(+), 284 deletions(-)

diff --git a/bindings/python/python/opendal/operator.pyi 
b/bindings/python/python/opendal/operator.pyi
index 9ae772a22..54c9f2f9e 100644
--- a/bindings/python/python/opendal/operator.pyi
+++ b/bindings/python/python/opendal/operator.pyi
@@ -504,6 +504,13 @@ class AsyncOperator:
             The blocking operator.
         """
     @typing.overload
+    def __new__(
+        cls,
+        scheme: builtins.str,
+        /,
+        **kwargs: builtins.str,
+    ) -> typing_extensions.Self: ...
+    @typing.overload
     def __new__(
         cls,
         scheme: opendal.services.Scheme.AliyunDrive | 
typing.Literal["aliyun-drive"],
@@ -2737,6 +2744,13 @@ class Operator:
             The async operator.
         """
     @typing.overload
+    def __new__(
+        cls,
+        scheme: builtins.str,
+        /,
+        **kwargs: builtins.str,
+    ) -> typing_extensions.Self: ...
+    @typing.overload
     def __new__(
         cls,
         scheme: opendal.services.Scheme.AliyunDrive | 
typing.Literal["aliyun-drive"],
diff --git a/bindings/python/src/operator.rs b/bindings/python/src/operator.rs
index c7c360240..cdc85ca2a 100644
--- a/bindings/python/src/operator.rs
+++ b/bindings/python/src/operator.rs
@@ -17,7 +17,6 @@
 
 use std::collections::HashMap;
 use std::path::PathBuf;
-use std::str::FromStr;
 use std::time::Duration;
 
 use pyo3::IntoPyObjectExt;
@@ -29,16 +28,13 @@ use pyo3_async_runtimes::tokio::future_into_py;
 
 use crate::*;
 
-fn build_operator(
-    scheme: ocore::Scheme,
-    map: HashMap<String, String>,
-) -> PyResult<ocore::Operator> {
+fn build_operator(scheme: &str, map: HashMap<String, String>) -> 
PyResult<ocore::Operator> {
     let op = ocore::Operator::via_iter(scheme, map).map_err(format_pyerr)?;
     Ok(op)
 }
 
 fn build_blocking_operator(
-    scheme: ocore::Scheme,
+    scheme: &str,
     map: HashMap<String, String>,
 ) -> PyResult<ocore::blocking::Operator> {
     let op = ocore::Operator::via_iter(scheme, map).map_err(format_pyerr)?;
@@ -60,7 +56,7 @@ fn build_blocking_operator(
 #[pyclass(module = "opendal.operator")]
 pub struct Operator {
     core: ocore::blocking::Operator,
-    __scheme: ocore::Scheme,
+    __scheme: String,
     __map: HashMap<String, String>,
 }
 
@@ -89,19 +85,14 @@ impl Operator {
         kwargs: Option<&Bound<PyDict>>,
     ) -> PyResult<Self> {
         let scheme = if let Ok(scheme_str) = scheme.extract::<&str>() {
-            ocore::Scheme::from_str(scheme_str)
-                .map_err(|err| {
-                    ocore::Error::new(ocore::ErrorKind::Unexpected, 
"unsupported scheme")
-                        .set_source(err)
-                })
-                .map_err(format_pyerr)
+            scheme_str.to_string()
         } else if let Ok(py_scheme) = scheme.extract::<PyScheme>() {
-            Ok(py_scheme.into())
+            String::from(py_scheme)
         } else {
-            Err(Unsupported::new_err(
+            return Err(Unsupported::new_err(
                 "Invalid type for scheme, expected str or Scheme",
-            ))
-        }?;
+            ));
+        };
         let map = kwargs
             .map(|v| {
                 v.extract::<HashMap<String, String>>()
@@ -110,7 +101,7 @@ impl Operator {
             .unwrap_or_default();
 
         Ok(Operator {
-            core: build_blocking_operator(scheme, map.clone())?,
+            core: build_blocking_operator(&scheme, map.clone())?,
             __scheme: scheme,
             __map: map,
         })
@@ -135,7 +126,7 @@ impl Operator {
         let op = ocore::blocking::Operator::new(op).map_err(format_pyerr)?;
         Ok(Self {
             core: op,
-            __scheme: self.__scheme,
+            __scheme: self.__scheme.clone(),
             __map: self.__map.clone(),
         })
     }
@@ -670,7 +661,7 @@ impl Operator {
     pub fn to_async_operator(&self) -> PyResult<AsyncOperator> {
         Ok(AsyncOperator {
             core: self.core.clone().into(),
-            __scheme: self.__scheme,
+            __scheme: self.__scheme.clone(),
             __map: self.__map.clone(),
         })
     }
@@ -691,7 +682,7 @@ impl Operator {
 
     #[gen_stub(skip)]
     fn __getnewargs_ex__(&self, py: Python) -> PyResult<Py<PyAny>> {
-        let args = vec![self.__scheme.to_string()];
+        let args = vec![self.__scheme.clone()];
         let args = PyTuple::new(py, args)?.into_py_any(py)?;
         let kwargs = self.__map.clone().into_py_any(py)?;
         PyTuple::new(py, [args, kwargs])?.into_py_any(py)
@@ -709,7 +700,7 @@ impl Operator {
 #[pyclass(module = "opendal.operator")]
 pub struct AsyncOperator {
     core: ocore::Operator,
-    __scheme: ocore::Scheme,
+    __scheme: String,
     __map: HashMap<String, String>,
 }
 
@@ -738,19 +729,14 @@ impl AsyncOperator {
         kwargs: Option<&Bound<PyDict>>,
     ) -> PyResult<Self> {
         let scheme = if let Ok(scheme_str) = scheme.extract::<&str>() {
-            ocore::Scheme::from_str(scheme_str)
-                .map_err(|err| {
-                    ocore::Error::new(ocore::ErrorKind::Unexpected, 
"unsupported scheme")
-                        .set_source(err)
-                })
-                .map_err(format_pyerr)
+            scheme_str.to_string()
         } else if let Ok(py_scheme) = scheme.extract::<PyScheme>() {
-            Ok(py_scheme.into())
+            String::from(py_scheme)
         } else {
-            Err(Unsupported::new_err(
+            return Err(Unsupported::new_err(
                 "Invalid type for scheme, expected str or Scheme",
-            ))
-        }?;
+            ));
+        };
 
         let map = kwargs
             .map(|v| {
@@ -760,7 +746,7 @@ impl AsyncOperator {
             .unwrap_or_default();
 
         Ok(AsyncOperator {
-            core: build_operator(scheme, map.clone())?,
+            core: build_operator(&scheme, map.clone())?,
             __scheme: scheme,
             __map: map,
         })
@@ -781,7 +767,7 @@ impl AsyncOperator {
         let op = layer.0.layer(self.core.clone());
         Ok(Self {
             core: op,
-            __scheme: self.__scheme,
+            __scheme: self.__scheme.clone(),
             __map: self.__map.clone(),
         })
     }
@@ -1608,7 +1594,7 @@ impl AsyncOperator {
 
         Ok(Operator {
             core: op,
-            __scheme: self.__scheme,
+            __scheme: self.__scheme.clone(),
             __map: self.__map.clone(),
         })
     }
@@ -1633,7 +1619,7 @@ impl AsyncOperator {
 
     #[gen_stub(skip)]
     fn __getnewargs_ex__(&self, py: Python) -> PyResult<Py<PyAny>> {
-        let args = vec![self.__scheme.to_string()];
+        let args = vec![self.__scheme.clone()];
         let args = PyTuple::new(py, args)?.into_py_any(py)?;
         let kwargs = self.__map.clone().into_py_any(py)?;
         PyTuple::new(py, [args, kwargs])?.into_py_any(py)
diff --git a/bindings/python/src/services.rs b/bindings/python/src/services.rs
index 719bee9f9..c9e48c678 100644
--- a/bindings/python/src/services.rs
+++ b/bindings/python/src/services.rs
@@ -32,6 +32,23 @@ See justfile at path ``../../justfile`` for more details.
 "#
 );
 
+submit! {
+    gen_methods_from_python! {
+        r#"
+        import builtins
+        import typing
+        import typing_extensions
+        class Operator:
+            @overload
+            def __new__(cls,
+                scheme: builtins.str,
+                /,
+                **kwargs: builtins.str,
+            ) -> typing_extensions.Self: ...
+        "#
+    }
+}
+
 #[gen_stub_pyclass_enum]
 #[pyclass(
     eq,
@@ -149,8 +166,7 @@ impl PyScheme {
 
     #[getter]
     pub fn value(&self) -> &'static str {
-        let scheme: ocore::Scheme = (*self).into();
-        scheme.into_static()
+        (*self).into()
     }
 }
 
@@ -1006,6 +1022,7 @@ submit! {
                 scheme: typing.Union[opendal.services.Scheme.Huggingface, 
typing.Literal["huggingface"]],
                 /,
                 *,
+                endpoint: builtins.str = ...,
                 repo_id: builtins.str = ...,
                 repo_type: builtins.str = ...,
                 revision: builtins.str = ...,
@@ -1017,13 +1034,17 @@ submit! {
 
                 Parameters
                 ----------
+                endpoint : builtins.str, optional
+                    Endpoint of the Huggingface Hub.
+                    Default is "https://huggingface.co";.
                 repo_id : builtins.str, optional
                     Repo id of this backend.
                     This is required.
                 repo_type : builtins.str, optional
                     Repo type of this backend.
                     Default is model.
-                    Available values: - model - dataset
+                    Available values: - model - dataset - datasets
+                    (alias for dataset)
                 revision : builtins.str, optional
                     Revision of this backend.
                     Default is main.
@@ -1897,7 +1918,7 @@ submit! {
                     HTTP headers.
                     This is necessary when writing to AWS S3 Buckets
                     with Object Lock enabled for example.
-                    Available options: - "crc32c"
+                    Available options: - "crc32c" - "md5"
                 default_storage_class : builtins.str, optional
                     default storage_class for this backend.
                     Available values: - `DEEP_ARCHIVE` - `GLACIER` -
@@ -1921,7 +1942,7 @@ submit! {
                     Disable load credential from ec2 metadata.
                     This option is used to disable the default behavior
                     of opendal to load credential from ec2 metadata,
-                    a.k.a, IMDSv2
+                    a.k.a., IMDSv2
                 disable_list_objects_v2 : builtins.bool, optional
                     OpenDAL uses List Objects V2 by default to list
                     objects.
@@ -1937,7 +1958,7 @@ submit! {
                     Disable write with if match so that opendal will not
                     send write request with if match headers.
                     For example, Ceph RADOS S3 doesn't support write
-                    with if match.
+                    with if matched.
                 enable_request_payer : builtins.bool, optional
                     Indicates whether the client agrees to pay for the
                     requests made to the S3 bucket.
@@ -2466,6 +2487,23 @@ submit! {
     }
 }
 
+submit! {
+    gen_methods_from_python! {
+        r#"
+        import builtins
+        import typing
+        import typing_extensions
+        class AsyncOperator:
+            @overload
+            def __new__(cls,
+                scheme: builtins.str,
+                /,
+                **kwargs: builtins.str,
+            ) -> typing_extensions.Self: ...
+        "#
+    }
+}
+
 submit! {
     gen_methods_from_python! {
         r#"
@@ -3318,6 +3356,7 @@ submit! {
                 scheme: typing.Union[opendal.services.Scheme.Huggingface, 
typing.Literal["huggingface"]],
                 /,
                 *,
+                endpoint: builtins.str = ...,
                 repo_id: builtins.str = ...,
                 repo_type: builtins.str = ...,
                 revision: builtins.str = ...,
@@ -3329,13 +3368,17 @@ submit! {
 
                 Parameters
                 ----------
+                endpoint : builtins.str, optional
+                    Endpoint of the Huggingface Hub.
+                    Default is "https://huggingface.co";.
                 repo_id : builtins.str, optional
                     Repo id of this backend.
                     This is required.
                 repo_type : builtins.str, optional
                     Repo type of this backend.
                     Default is model.
-                    Available values: - model - dataset
+                    Available values: - model - dataset - datasets
+                    (alias for dataset)
                 revision : builtins.str, optional
                     Revision of this backend.
                     Default is main.
@@ -4209,7 +4252,7 @@ submit! {
                     HTTP headers.
                     This is necessary when writing to AWS S3 Buckets
                     with Object Lock enabled for example.
-                    Available options: - "crc32c"
+                    Available options: - "crc32c" - "md5"
                 default_storage_class : builtins.str, optional
                     default storage_class for this backend.
                     Available values: - `DEEP_ARCHIVE` - `GLACIER` -
@@ -4233,7 +4276,7 @@ submit! {
                     Disable load credential from ec2 metadata.
                     This option is used to disable the default behavior
                     of opendal to load credential from ec2 metadata,
-                    a.k.a, IMDSv2
+                    a.k.a., IMDSv2
                 disable_list_objects_v2 : builtins.bool, optional
                     OpenDAL uses List Objects V2 by default to list
                     objects.
@@ -4249,7 +4292,7 @@ submit! {
                     Disable write with if match so that opendal will not
                     send write request with if match headers.
                     For example, Ceph RADOS S3 doesn't support write
-                    with if match.
+                    with if matched.
                 enable_request_payer : builtins.bool, optional
                     Indicates whether the client agrees to pay for the
                     requests made to the S3 bucket.
@@ -4778,225 +4821,124 @@ submit! {
     }
 }
 
-// --- Conversion Macro ---
-macro_rules! impl_enum_from {
-    ($src:ty => $dst:ty { $(
+macro_rules! impl_enum_to_str {
+    ($src:ty { $(
         $(#[$cfg:meta])?
-        $variant:ident
+        $variant:ident => $value:literal
     ),* $(,)? }) => {
-        impl From<$src> for $dst {
+        impl From<$src> for &'static str {
             fn from(value: $src) -> Self {
                 match value {
                     $(
                         $(#[$cfg])?
-                        <$src>::$variant => <$dst>::$variant,
+                        <$src>::$variant => $value,
                     )*
-                    #[allow(unreachable_patterns)]
-                    _ => unreachable!(
-                        "Unsupported scheme variant: {:?}. \
-                         This likely means a new variant was added to `{}` \
-                         but `PyScheme` or the generated bindings were not 
updated.",
-                        value,
-                        stringify!($src)
-                    ),
                 }
             }
         }
+
+        impl From<$src> for String {
+            fn from(value: $src) -> Self {
+                let v: &'static str = value.into();
+                v.to_string()
+            }
+        }
     };
 }
 
-// --- PyScheme -> ocore::Scheme ---
-impl_enum_from!(
-    PyScheme => ocore::Scheme {
-    #[cfg(feature = "services-aliyun-drive")]
-    AliyunDrive,
-    #[cfg(feature = "services-alluxio")]
-    Alluxio,
-    #[cfg(feature = "services-azblob")]
-    Azblob,
-    #[cfg(feature = "services-azdls")]
-    Azdls,
-    #[cfg(feature = "services-azfile")]
-    Azfile,
-    #[cfg(feature = "services-b2")]
-    B2,
-    #[cfg(feature = "services-cacache")]
-    Cacache,
-    #[cfg(feature = "services-cos")]
-    Cos,
-    #[cfg(feature = "services-dashmap")]
-    Dashmap,
-    #[cfg(feature = "services-dropbox")]
-    Dropbox,
-    #[cfg(feature = "services-fs")]
-    Fs,
-    #[cfg(feature = "services-ftp")]
-    Ftp,
-    #[cfg(feature = "services-gcs")]
-    Gcs,
-    #[cfg(feature = "services-gdrive")]
-    Gdrive,
-    #[cfg(feature = "services-ghac")]
-    Ghac,
-    #[cfg(feature = "services-gridfs")]
-    Gridfs,
-    #[cfg(feature = "services-hdfs-native")]
-    HdfsNative,
-    #[cfg(feature = "services-http")]
-    Http,
-    #[cfg(feature = "services-huggingface")]
-    Huggingface,
-    #[cfg(feature = "services-ipfs")]
-    Ipfs,
-    #[cfg(feature = "services-ipmfs")]
-    Ipmfs,
-    #[cfg(feature = "services-koofr")]
-    Koofr,
-    #[cfg(feature = "services-memcached")]
-    Memcached,
-    #[cfg(feature = "services-memory")]
-    Memory,
-    #[cfg(feature = "services-mini-moka")]
-    MiniMoka,
-    #[cfg(feature = "services-moka")]
-    Moka,
-    #[cfg(feature = "services-mongodb")]
-    Mongodb,
-    #[cfg(feature = "services-mysql")]
-    Mysql,
-    #[cfg(feature = "services-obs")]
-    Obs,
-    #[cfg(feature = "services-onedrive")]
-    Onedrive,
-    #[cfg(feature = "services-oss")]
-    Oss,
-    #[cfg(feature = "services-persy")]
-    Persy,
-    #[cfg(feature = "services-postgresql")]
-    Postgresql,
-    #[cfg(feature = "services-redb")]
-    Redb,
-    #[cfg(feature = "services-redis")]
-    Redis,
-    #[cfg(feature = "services-s3")]
-    S3,
-    #[cfg(feature = "services-seafile")]
-    Seafile,
-    #[cfg(feature = "services-sftp")]
-    Sftp,
-    #[cfg(feature = "services-sled")]
-    Sled,
-    #[cfg(feature = "services-sqlite")]
-    Sqlite,
-    #[cfg(feature = "services-swift")]
-    Swift,
-    #[cfg(feature = "services-upyun")]
-    Upyun,
-    #[cfg(feature = "services-vercel-artifacts")]
-    VercelArtifacts,
-    #[cfg(feature = "services-webdav")]
-    Webdav,
-    #[cfg(feature = "services-webhdfs")]
-    Webhdfs,
-    #[cfg(feature = "services-yandex-disk")]
-    YandexDisk,
-    }
-);
-
-// --- ocore::Scheme -> PyScheme ---
-impl_enum_from!(
-    ocore::Scheme => PyScheme {
-    #[cfg(feature = "services-aliyun-drive")]
-    AliyunDrive,
-    #[cfg(feature = "services-alluxio")]
-    Alluxio,
-    #[cfg(feature = "services-azblob")]
-    Azblob,
-    #[cfg(feature = "services-azdls")]
-    Azdls,
-    #[cfg(feature = "services-azfile")]
-    Azfile,
-    #[cfg(feature = "services-b2")]
-    B2,
-    #[cfg(feature = "services-cacache")]
-    Cacache,
-    #[cfg(feature = "services-cos")]
-    Cos,
-    #[cfg(feature = "services-dashmap")]
-    Dashmap,
-    #[cfg(feature = "services-dropbox")]
-    Dropbox,
-    #[cfg(feature = "services-fs")]
-    Fs,
-    #[cfg(feature = "services-ftp")]
-    Ftp,
-    #[cfg(feature = "services-gcs")]
-    Gcs,
-    #[cfg(feature = "services-gdrive")]
-    Gdrive,
-    #[cfg(feature = "services-ghac")]
-    Ghac,
-    #[cfg(feature = "services-gridfs")]
-    Gridfs,
-    #[cfg(feature = "services-hdfs-native")]
-    HdfsNative,
-    #[cfg(feature = "services-http")]
-    Http,
-    #[cfg(feature = "services-huggingface")]
-    Huggingface,
-    #[cfg(feature = "services-ipfs")]
-    Ipfs,
-    #[cfg(feature = "services-ipmfs")]
-    Ipmfs,
-    #[cfg(feature = "services-koofr")]
-    Koofr,
-    #[cfg(feature = "services-memcached")]
-    Memcached,
-    #[cfg(feature = "services-memory")]
-    Memory,
-    #[cfg(feature = "services-mini-moka")]
-    MiniMoka,
-    #[cfg(feature = "services-moka")]
-    Moka,
-    #[cfg(feature = "services-mongodb")]
-    Mongodb,
-    #[cfg(feature = "services-mysql")]
-    Mysql,
-    #[cfg(feature = "services-obs")]
-    Obs,
-    #[cfg(feature = "services-onedrive")]
-    Onedrive,
-    #[cfg(feature = "services-oss")]
-    Oss,
-    #[cfg(feature = "services-persy")]
-    Persy,
-    #[cfg(feature = "services-postgresql")]
-    Postgresql,
-    #[cfg(feature = "services-redb")]
-    Redb,
-    #[cfg(feature = "services-redis")]
-    Redis,
-    #[cfg(feature = "services-s3")]
-    S3,
-    #[cfg(feature = "services-seafile")]
-    Seafile,
-    #[cfg(feature = "services-sftp")]
-    Sftp,
-    #[cfg(feature = "services-sled")]
-    Sled,
-    #[cfg(feature = "services-sqlite")]
-    Sqlite,
-    #[cfg(feature = "services-swift")]
-    Swift,
-    #[cfg(feature = "services-upyun")]
-    Upyun,
-    #[cfg(feature = "services-vercel-artifacts")]
-    VercelArtifacts,
-    #[cfg(feature = "services-webdav")]
-    Webdav,
-    #[cfg(feature = "services-webhdfs")]
-    Webhdfs,
-    #[cfg(feature = "services-yandex-disk")]
-    YandexDisk,
+impl_enum_to_str!(
+    PyScheme {
+        #[cfg(feature = "services-aliyun-drive")]
+        AliyunDrive => "aliyun-drive",
+        #[cfg(feature = "services-alluxio")]
+        Alluxio => "alluxio",
+        #[cfg(feature = "services-azblob")]
+        Azblob => "azblob",
+        #[cfg(feature = "services-azdls")]
+        Azdls => "azdls",
+        #[cfg(feature = "services-azfile")]
+        Azfile => "azfile",
+        #[cfg(feature = "services-b2")]
+        B2 => "b2",
+        #[cfg(feature = "services-cacache")]
+        Cacache => "cacache",
+        #[cfg(feature = "services-cos")]
+        Cos => "cos",
+        #[cfg(feature = "services-dashmap")]
+        Dashmap => "dashmap",
+        #[cfg(feature = "services-dropbox")]
+        Dropbox => "dropbox",
+        #[cfg(feature = "services-fs")]
+        Fs => "fs",
+        #[cfg(feature = "services-ftp")]
+        Ftp => "ftp",
+        #[cfg(feature = "services-gcs")]
+        Gcs => "gcs",
+        #[cfg(feature = "services-gdrive")]
+        Gdrive => "gdrive",
+        #[cfg(feature = "services-ghac")]
+        Ghac => "ghac",
+        #[cfg(feature = "services-gridfs")]
+        Gridfs => "gridfs",
+        #[cfg(feature = "services-hdfs-native")]
+        HdfsNative => "hdfs-native",
+        #[cfg(feature = "services-http")]
+        Http => "http",
+        #[cfg(feature = "services-huggingface")]
+        Huggingface => "huggingface",
+        #[cfg(feature = "services-ipfs")]
+        Ipfs => "ipfs",
+        #[cfg(feature = "services-ipmfs")]
+        Ipmfs => "ipmfs",
+        #[cfg(feature = "services-koofr")]
+        Koofr => "koofr",
+        #[cfg(feature = "services-memcached")]
+        Memcached => "memcached",
+        #[cfg(feature = "services-memory")]
+        Memory => "memory",
+        #[cfg(feature = "services-mini-moka")]
+        MiniMoka => "mini-moka",
+        #[cfg(feature = "services-moka")]
+        Moka => "moka",
+        #[cfg(feature = "services-mongodb")]
+        Mongodb => "mongodb",
+        #[cfg(feature = "services-mysql")]
+        Mysql => "mysql",
+        #[cfg(feature = "services-obs")]
+        Obs => "obs",
+        #[cfg(feature = "services-onedrive")]
+        Onedrive => "onedrive",
+        #[cfg(feature = "services-oss")]
+        Oss => "oss",
+        #[cfg(feature = "services-persy")]
+        Persy => "persy",
+        #[cfg(feature = "services-postgresql")]
+        Postgresql => "postgresql",
+        #[cfg(feature = "services-redb")]
+        Redb => "redb",
+        #[cfg(feature = "services-redis")]
+        Redis => "redis",
+        #[cfg(feature = "services-s3")]
+        S3 => "s3",
+        #[cfg(feature = "services-seafile")]
+        Seafile => "seafile",
+        #[cfg(feature = "services-sftp")]
+        Sftp => "sftp",
+        #[cfg(feature = "services-sled")]
+        Sled => "sled",
+        #[cfg(feature = "services-sqlite")]
+        Sqlite => "sqlite",
+        #[cfg(feature = "services-swift")]
+        Swift => "swift",
+        #[cfg(feature = "services-upyun")]
+        Upyun => "upyun",
+        #[cfg(feature = "services-vercel-artifacts")]
+        VercelArtifacts => "vercel-artifacts",
+        #[cfg(feature = "services-webdav")]
+        Webdav => "webdav",
+        #[cfg(feature = "services-webhdfs")]
+        Webhdfs => "webhdfs",
+        #[cfg(feature = "services-yandex-disk")]
+        YandexDisk => "yandex-disk",
     }
 );
diff --git a/dev/src/generate/python.j2 b/dev/src/generate/python.j2
index cab42dffb..fabd4807a 100644
--- a/dev/src/generate/python.j2
+++ b/dev/src/generate/python.j2
@@ -32,6 +32,23 @@ See justfile at path ``../../justfile`` for more details.
 "#
 );
 
+submit! {
+    gen_methods_from_python! {
+        r#"
+        import builtins
+        import typing
+        import typing_extensions
+        class Operator:
+            @overload
+            def __new__(cls,
+                scheme: builtins.str,
+                /,
+                **kwargs: builtins.str,
+            ) -> typing_extensions.Self: ...
+        "#
+    }
+}
+
 #[gen_stub_pyclass_enum]
 #[pyclass(
     eq,
@@ -61,8 +78,7 @@ impl PyScheme {
 
     #[getter]
     pub fn value(&self) -> &'static str {
-        let scheme: ocore::Scheme = (*self).into();
-        scheme.into_static()
+        (*self).into()
     }
 }
 
@@ -107,6 +123,22 @@ submit! {
 }
 
 {% endfor %}
+submit! {
+    gen_methods_from_python! {
+        r#"
+        import builtins
+        import typing
+        import typing_extensions
+        class AsyncOperator:
+            @overload
+            def __new__(cls,
+                scheme: builtins.str,
+                /,
+                **kwargs: builtins.str,
+            ) -> typing_extensions.Self: ...
+        "#
+    }
+}
 {% for srv in srvs %}
 submit! {
     gen_methods_from_python! {
@@ -147,49 +179,36 @@ submit! {
     }
 }
 {% endfor %}
-// --- Conversion Macro ---
-macro_rules! impl_enum_from {
-    ($src:ty => $dst:ty { $(
+macro_rules! impl_enum_to_str {
+    ($src:ty { $(
         $(#[$cfg:meta])?
-        $variant:ident
+        $variant:ident => $value:literal
     ),* $(,)? }) => {
-        impl From<$src> for $dst {
+        impl From<$src> for &'static str {
             fn from(value: $src) -> Self {
                 match value {
                     $(
                         $(#[$cfg])?
-                        <$src>::$variant => <$dst>::$variant,
+                        <$src>::$variant => $value,
                     )*
-                    #[allow(unreachable_patterns)]
-                    _ => unreachable!(
-                        "Unsupported scheme variant: {:?}. \
-                         This likely means a new variant was added to `{}` \
-                         but `PyScheme` or the generated bindings were not 
updated.",
-                        value,
-                        stringify!($src)
-                    ),
                 }
             }
         }
+
+        impl From<$src> for String {
+            fn from(value: $src) -> Self {
+                let v: &'static str = value.into();
+                v.to_string()
+            }
+        }
     };
 }
 
-// --- PyScheme -> ocore::Scheme ---
-impl_enum_from!(
-    PyScheme => ocore::Scheme {
+impl_enum_to_str!(
+    PyScheme {
 {%- for name in srvs %}
-    #[cfg(feature = "{{ service_to_feature(name) }}")]
-    {{ service_to_pascal(name) }},
-{%- endfor %}
-    }
-);
-
-// --- ocore::Scheme -> PyScheme ---
-impl_enum_from!(
-    ocore::Scheme => PyScheme {
-{%- for name in srvs %}
-    #[cfg(feature = "{{ service_to_feature(name) }}")]
-    {{ service_to_pascal(name) }},
+        #[cfg(feature = "{{ service_to_feature(name) }}")]
+        {{ service_to_pascal(name) }} => "{{ snake_to_kebab_case(name) }}",
 {%- endfor %}
     }
 );

Reply via email to