@@ -436,6 +436,39 @@ pub(super) mod _os {
436436 Ok ( ( ) )
437437 }
438438
439+ #[ cfg( windows) ]
440+ #[ pyfunction]
441+ fn putenv ( key : PyStrRef , value : PyStrRef , vm : & VirtualMachine ) -> PyResult < ( ) > {
442+ use crate :: common:: windows:: _MAX_ENV;
443+ let key_str = key. as_str ( ) ;
444+ let value_str = value. as_str ( ) ;
445+ // Search from index 1 because on Windows starting '=' is allowed for
446+ // defining hidden environment variables.
447+ if key_str. is_empty ( )
448+ || key_str. get ( 1 ..) . is_some_and ( |s| s. contains ( '=' ) )
449+ || key_str. contains ( '\0' )
450+ || value_str. contains ( '\0' )
451+ {
452+ return Err ( vm. new_value_error ( "illegal environment variable name" ) ) ;
453+ }
454+ let env_str = format ! ( "{}={}" , key_str, value_str) ;
455+ let wide = env_str. to_wide_with_nul ( ) ;
456+ if wide. len ( ) > _MAX_ENV + 1 {
457+ return Err ( vm. new_value_error ( format ! (
458+ "the environment variable is longer than {} characters" ,
459+ _MAX_ENV
460+ ) ) ) ;
461+ }
462+
463+ // Use _wputenv like CPython (not SetEnvironmentVariableW) to update CRT environ
464+ let result = unsafe { suppress_iph ! ( libc:: wputenv( wide. as_ptr( ) ) ) } ;
465+ if result != 0 {
466+ return Err ( vm. new_last_errno_error ( ) ) ;
467+ }
468+ Ok ( ( ) )
469+ }
470+
471+ #[ cfg( not( windows) ) ]
439472 #[ pyfunction]
440473 fn putenv (
441474 key : Either < PyStrRef , PyBytesRef > ,
@@ -450,15 +483,45 @@ pub(super) mod _os {
450483 if key. is_empty ( ) || key. contains ( & b'=' ) {
451484 return Err ( vm. new_value_error ( "illegal environment variable name" ) ) ;
452485 }
453- #[ cfg( windows) ]
454- check_env_var_len ( key. len ( ) + value. len ( ) + 2 , vm) ?;
455486 let key = super :: bytes_as_os_str ( key, vm) ?;
456487 let value = super :: bytes_as_os_str ( value, vm) ?;
457488 // SAFETY: requirements forwarded from the caller
458489 unsafe { env:: set_var ( key, value) } ;
459490 Ok ( ( ) )
460491 }
461492
493+ #[ cfg( windows) ]
494+ #[ pyfunction]
495+ fn unsetenv ( key : PyStrRef , vm : & VirtualMachine ) -> PyResult < ( ) > {
496+ use crate :: common:: windows:: _MAX_ENV;
497+ let key_str = key. as_str ( ) ;
498+ // Search from index 1 because on Windows starting '=' is allowed for
499+ // defining hidden environment variables.
500+ if key_str. is_empty ( )
501+ || key_str. get ( 1 ..) . is_some_and ( |s| s. contains ( '=' ) )
502+ || key_str. contains ( '\0' )
503+ {
504+ return Err ( vm. new_value_error ( "illegal environment variable name" ) ) ;
505+ }
506+ // "key=" to unset (empty value removes the variable)
507+ let env_str = format ! ( "{}=" , key_str) ;
508+ let wide = env_str. to_wide_with_nul ( ) ;
509+ if wide. len ( ) > _MAX_ENV + 1 {
510+ return Err ( vm. new_value_error ( format ! (
511+ "the environment variable is longer than {} characters" ,
512+ _MAX_ENV
513+ ) ) ) ;
514+ }
515+
516+ // Use _wputenv like CPython (not SetEnvironmentVariableW) to update CRT environ
517+ let result = unsafe { suppress_iph ! ( libc:: wputenv( wide. as_ptr( ) ) ) } ;
518+ if result != 0 {
519+ return Err ( vm. new_last_errno_error ( ) ) ;
520+ }
521+ Ok ( ( ) )
522+ }
523+
524+ #[ cfg( not( windows) ) ]
462525 #[ pyfunction]
463526 fn unsetenv ( key : Either < PyStrRef , PyBytesRef > , vm : & VirtualMachine ) -> PyResult < ( ) > {
464527 let key = env_bytes_as_bytes ( & key) ;
@@ -474,11 +537,6 @@ pub(super) mod _os {
474537 ) ,
475538 ) ) ;
476539 }
477- #[ cfg( windows) ]
478- {
479- // For unsetenv, size is key + '=' (no value, just clearing)
480- check_env_var_len ( key. len ( ) + 1 , vm) ?;
481- }
482540 let key = super :: bytes_as_os_str ( key, vm) ?;
483541 // SAFETY: requirements forwarded from the caller
484542 unsafe { env:: remove_var ( key) } ;
0 commit comments