¿Solicitar elevación de UAC desde dentro de un script de Python?

Quiero que mi script de Python copie archivos en Vista. Cuando lo ejecuto desde una ventana de cmd.exe normal, no se generan errores, sin embargo, los archivos NO se copian. Si ejecuto cmd.exe “como administrador” y luego ejecuto mi script, funciona bien.

Esto tiene sentido ya que el Control de cuentas de usuario (UAC) normalmente evita muchas acciones del sistema de archivos.

¿Hay alguna forma en que pueda, desde un script de Python, invocar una solicitud de elevación de UAC (los diálogos que dicen algo como “tal y cual aplicación necesita acceso de administrador, está bien?”)

Si eso no es posible, ¿hay alguna forma en que mi script pueda al menos detectar que no está elevado para que pueda fallar con gracia?

A partir de 2017, un método fácil para lograrlo es el siguiente:

 import ctypes, sys def is_admin(): try: return ctypes.windll.shell32.IsUserAnAdmin() except: return False if is_admin(): # Code of your program here else: # Re-run the program with admin rights ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1) 

Si está usando Python 2.x, entonces debe reemplazar la última línea por:

 ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(__file__), None, 1) 

También tenga en cuenta que si convirtió su script de Python en un archivo ejecutable (utilizando herramientas como py2exe , cx_freeze , pyinstaller ), entonces debería reemplazar el cuarto parámetro por una cadena vacía ( "" ).

Algunas de las ventajas aquí son:

  • No se requieren bibliotecas externas (ni Python para la extensión de Windows). Solo usa ctypes de la librería estándar.
  • Funciona tanto en Python 2 como en Python 3.
  • No es necesario modificar los recursos del archivo ni crear un archivo de manifiesto.
  • Si no agrega el código debajo de la sentencia / else, el código nunca se ejecutará dos veces.
  • Puede modificarlo fácilmente para que tenga un comportamiento especial si el usuario rechaza la solicitud de UAC.
  • Puede especificar argumentos modificando el cuarto parámetro.
  • Puede especificar el método de visualización modificando el sexto parámetro.

La documentación para la llamada ShellExecute subyacente está aquí .

Me tomó un poco de tiempo hacer que la respuesta de Dguaraglia funcionara, así que para ahorrar tiempo a los demás, esto es lo que hice para implementar esta idea:

 import os import sys import win32com.shell.shell as shell ASADMIN = 'asadmin' if sys.argv[-1] != ASADMIN: script = os.path.abspath(sys.argv[0]) params = ' '.join([script] + sys.argv[1:] + [ASADMIN]) shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params) sys.exit(0) 

Parece que no hay forma de elevar los privilegios de la aplicación por un tiempo para que usted realice una tarea en particular. Windows necesita saber al inicio del progtwig si la aplicación requiere ciertos privilegios, y le pedirá al usuario que confirme cuándo la aplicación realiza alguna tarea que necesite esos privilegios. Hay dos maneras de hacer esto:

  1. Escriba un archivo de manifiesto que le indique a Windows que la aplicación podría requerir algunos privilegios
  2. Ejecuta la aplicación con privilegios elevados desde otro progtwig.

Estos dos artículos explican con mucho más detalle cómo funciona esto.

Lo que haría, si no desea escribir un desagradable envoltorio de ctypes para la API CreateElevatedProcess, es usar el truco de ShellExecuteEx explicado en el artículo del Proyecto de Código (Pywin32 viene con un envoltorio para ShellExecute). ¿Cómo? Algo como esto:

Cuando se inicia el progtwig, comprueba si tiene privilegios de administrador, si no lo hace, se ejecuta solo con el truco de ShellExecute y sale de inmediato, si lo hace, realiza la tarea en cuestión.

Al describir su progtwig como un “script”, supongo que es suficiente para sus necesidades.

Aclamaciones.

Reconociendo esta pregunta hace años, creo que una solución más elegante se ofrece en github por frmdstryr usando su módulo pyminutils:

Extracto:

 import pythoncom from win32com.shell import shell,shellcon def copy(src,dst,flags=shellcon.FOF_NOCONFIRMATION): """ Copy files using the built in Windows File copy dialog Requires absolute paths. Does NOT create root destination folder if it doesn't exist. Overwrites and is recursive by default @see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx for flags available """ # @see IFileOperation pfo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation,None,pythoncom.CLSCTX_ALL,shell.IID_IFileOperation) # Respond with Yes to All for any dialog # @see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx pfo.SetOperationFlags(flags) # Set the destionation folder dst = shell.SHCreateItemFromParsingName(dst,None,shell.IID_IShellItem) if type(src) not in (tuple,list): src = (src,) for f in src: item = shell.SHCreateItemFromParsingName(f,None,shell.IID_IShellItem) pfo.CopyItem(item,dst) # Schedule an operation to be performed # @see http://msdn.microsoft.com/en-us/library/bb775780(v=vs.85).aspx success = pfo.PerformOperations() # @see sdn.microsoft.com/en-us/library/bb775769(v=vs.85).aspx aborted = pfo.GetAnyOperationsAborted() return success is None and not aborted 

Esto utiliza la interfaz COM e indica automáticamente que se necesitan privilegios de administrador con el mensaje de diálogo familiar que vería si estuviera copiando en un directorio donde se requieren privilegios de administrador y también proporciona el diálogo típico de progreso del archivo durante la operación de copia.

El siguiente ejemplo se basa en el excelente trabajo y la respuesta aceptada de MARTIN DE LA FUENTE SAAVEDRA . En particular, se introducen dos enumeraciones. La primera permite una fácil especificación de cómo se abrirá un progtwig elevado, y la segunda ayuda cuando los errores deben identificarse fácilmente. Tenga en cuenta que si desea que todos los argumentos de la línea de comandos pasen al nuevo proceso, sys.argv[0] probablemente debería reemplazarse con una llamada a la función: subprocess.list2cmdline(sys.argv) .

 #! /usr/bin/env python3 import ctypes import enum import subprocess import sys # Reference: # msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx # noinspection SpellCheckingInspection class SW(enum.IntEnum): HIDE = 0 MAXIMIZE = 3 MINIMIZE = 6 RESTORE = 9 SHOW = 5 SHOWDEFAULT = 10 SHOWMAXIMIZED = 3 SHOWMINIMIZED = 2 SHOWMINNOACTIVE = 7 SHOWNA = 8 SHOWNOACTIVATE = 4 SHOWNORMAL = 1 class ERROR(enum.IntEnum): ZERO = 0 FILE_NOT_FOUND = 2 PATH_NOT_FOUND = 3 BAD_FORMAT = 11 ACCESS_DENIED = 5 ASSOC_INCOMPLETE = 27 DDE_BUSY = 30 DDE_FAIL = 29 DDE_TIMEOUT = 28 DLL_NOT_FOUND = 32 NO_ASSOC = 31 OOM = 8 SHARE = 26 def bootstrap(): if ctypes.windll.shell32.IsUserAnAdmin(): main() else: # noinspection SpellCheckingInspection hinstance = ctypes.windll.shell32.ShellExecuteW( None, 'runas', sys.executable, subprocess.list2cmdline(sys.argv), None, SW.SHOWNORMAL ) if hinstance <= 32: raise RuntimeError(ERROR(hinstance)) def main(): # Your Code Here print(input('Echo: ')) if __name__ == '__main__': bootstrap() 

Es posible que esto no responda por completo a su pregunta, pero también puede intentar usar el comando Elevate Command Powertoy para ejecutar el script con privilegios de UAC elevados.

http://technet.microsoft.com/en-us/magazine/2008.06.elevation.aspx

Creo que si lo usas se vería como ‘elevate python yourscript.py’

Si su script siempre requiere privilegios de administrador, entonces:

 runas /user:Administrator "python your_script.py" 

Puede hacer un acceso directo en alguna parte y como uso del destino: python yourscript.py luego en propiedades y selección avanzada ejecutar como administrador.

Cuando el usuario ejecuta el acceso directo, le pedirá que eleve la aplicación.

Una variación en el trabajo de Jorenko anterior permite que el proceso elevado use la misma consola (pero vea mi comentario a continuación):

 def spawn_as_administrator(): """ Spawn ourself with administrator rights and wait for new process to exit Make the new process use the same console as the old one. Raise Exception() if we could not get a handle for the new re-run the process Raise pywintypes.error() if we could not re-spawn Return the exit code of the new process, or return None if already running the second admin process. """ #pylint: disable=no-name-in-module,import-error import win32event, win32api, win32process import win32com.shell.shell as shell if '--admin' in sys.argv: return None script = os.path.abspath(sys.argv[0]) params = ' '.join([script] + sys.argv[1:] + ['--admin']) SEE_MASK_NO_CONSOLE = 0x00008000 SEE_MASK_NOCLOSE_PROCESS = 0x00000040 process = shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params, fMask=SEE_MASK_NO_CONSOLE|SEE_MASK_NOCLOSE_PROCESS) hProcess = process['hProcess'] if not hProcess: raise Exception("Could not identify administrator process to install drivers") # It is necessary to wait for the elevated process or else # stdin lines are shared between 2 processes: they get one line each INFINITE = -1 win32event.WaitForSingleObject(hProcess, INFINITE) exitcode = win32process.GetExitCodeProcess(hProcess) win32api.CloseHandle(hProcess) return exitcode 

Esto es principalmente una actualización a la respuesta de Jorenko, que permite usar parámetros con espacios en Windows, pero también debería funcionar bastante bien en Linux 🙂 Además, funcionará con cx_freeze o py2exe ya que no usamos __file__ pero sys.argv[0] como ejecutable

 import sys,ctypes,platform def is_admin(): try: return ctypes.windll.shell32.IsUserAnAdmin() except: raise False if __name__ == '__main__': if platform.system() == "Windows": if is_admin(): main(sys.argv[1:]) else: # Re-run the program with admin rights, don't use __file__ since py2exe won't know about it # Use sys.argv[0] as script path and sys.argv[1:] as arguments, join them as lpstr, quoting each parameter or spaces will divide parameters lpParameters = "" # Litteraly quote all parameters which get unquoted when passed to python for i, item in enumerate(sys.argv[0:]): lpParameters += '"' + item + '" ' try: ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, lpParameters , None, 1) except: sys.exit(1) else: main(sys.argv[1:])