Repository: RustPython/RustPython (language: rust)

Issue:
Enhance compatibility of os.pathconf_names
Placeholder to track the implmentation of os.pathconf_names  -- additional detail to follow
(related to Extend the os module #1175)


Discussion / hints:
Here is the approach:
The pathconf data is stored in PathconVar Enum, which is configured 'manually' by setting the value  constants from libc  (which is the integer value needed for pathconf)  Since its a simple enum (no data associated) each enum variant can converted to an integer representation using 'as u8/u16/...'

I'd want the code to iterate through Enum creating the data and create the string and the integer value to build the dictionary.

I initially looked at a proc_macro to do this, but then saw that pathconf uses a create strum_macro::EnumString to the variatnt name as string  and there is another option EnumVariantNames which already support iterating through the different variants as str references (&&str).

And the code will look like this (I followed the os.envion dictionary as an example):
```
      #[pyattr]
      fn pathconf_names(vm: &VirtualMachine) -> PyDictRef {
          let pathconf_name_dict = vm.ctx.new_dict();
          // iterate through each item 
          for variant in PathconfVar::VARIANTS {
              // get the name of variant as a string to use as the dictionary key
              let key: PyObjectRef = vm.ctx.new_str(variant.to_string()).into();\
              // get the enum value itself  and convert it to an integer for he dictionary value 
              let value: PyObjectRef = vm
                  .ctx
                  .new_int(PathconfVar::from_str(variant).unwrap() as u8)
                  .into();
              pathconf_name_dict.set_item(&*key, value, vm).unwrap();
          }
          pathname_name_dict
      }

```
 

Fix patch:
diff --git a/vm/src/stdlib/posix.rs b/vm/src/stdlib/posix.rs
index 7f14b2db56..ea3e6b9d8d 100644
--- a/vm/src/stdlib/posix.rs
+++ b/vm/src/stdlib/posix.rs
@@ -51,7 +51,7 @@ pub mod module {
         fs, io,
         os::unix::{ffi as ffi_ext, io::RawFd},
     };
-    use strum_macros::EnumString;
+    use strum_macros::{EnumString, EnumVariantNames};
 
     #[pyattr]
     use libc::{PRIO_PGRP, PRIO_PROCESS, PRIO_USER};
@@ -1681,7 +1681,7 @@ pub mod module {
 
     // Copy from [nix::unistd::PathconfVar](https://docs.rs/nix/0.21.0/nix/unistd/enum.PathconfVar.html)
     // Change enum name to fit python doc
-    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, EnumString)]
+    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, EnumString, EnumVariantNames)]
     #[repr(i32)]
     #[allow(non_camel_case_types)]
     pub enum PathconfVar {
@@ -1881,6 +1881,26 @@ pub mod module {
         pathconf(PathOrFd::Fd(fd), name, vm)
     }
 
+    // TODO: this is expected to be run on macOS as a unix, but somehow not.
+    #[cfg(target_os = "linux")]
+    #[pyattr]
+    fn pathconf_names(vm: &VirtualMachine) -> PyDictRef {
+        use std::str::FromStr;
+        use strum::VariantNames;
+        let pathname = vm.ctx.new_dict();
+        for variant in PathconfVar::VARIANTS {
+            // get the name of variant as a string to use as the dictionary key
+            let key = vm.ctx.new_str(variant.to_string());
+            // get the enum from the string and convert it to an integer for the dictionary value
+            let value: PyObjectRef = vm
+                .ctx
+                .new_int(PathconfVar::from_str(variant).unwrap() as u8)
+                .into();
+            pathname.set_item(&*key, value, vm).unwrap();
+        }
+        pathname
+    }
+
     #[cfg(any(target_os = "linux", target_os = "macos"))]
     #[derive(FromArgs)]
     struct SendFileArgs {


Test patch:
diff --git a/extra_tests/snippets/stdlib_os.py b/extra_tests/snippets/stdlib_os.py
index 4c0d28e3cb..6eadeb19e0 100644
--- a/extra_tests/snippets/stdlib_os.py
+++ b/extra_tests/snippets/stdlib_os.py
@@ -507,3 +507,10 @@ def __exit__(self, exc_type, exc_val, exc_tb):
 
     for arg in [None, 1, 1.0, TabError]:
         assert_raises(TypeError, os.system, arg)
+
+# Testing for os.pathconf_names
+if sys.platform.startswith('linux'):
+    assert len(os.pathconf_names) > 0
+    assert 'PC_NAME_MAX' in os.pathconf_names
+    for option,index in os.pathconf_names.items():
+        assert os.pathconf('/', index) == os.pathconf('/', option)