2011-05-29 8 views
9

Tengo un módulo C puro para Python y me gustaría poder invocarlo utilizando el enfoque python -m modulename. Esto funciona bien con los módulos implementados en Python y una solución obvia es agregar un archivo adicional para ese fin. Sin embargo, yo realmente quiero mantener las cosas en mi único binario distribuido y no agregar un segundo archivo solo para esta solución.Obtención del módulo python -m para un módulo implementado en C

No me importa qué tan hacky es la solución.

Si intenta utilizar un módulo C con -m, aparece un mensaje de error No code object available for <modulename>.

+0

¿Qué herramientas usas, qué estás tratando de lograr, cuál es la plataforma? ¿Qué cantidad de una biblioteca estándar usas? ¿Es el módulo tuyo y de código cerrado, o algo que podemos ver? No puedo ayudar sin información. – janislaw

+0

Gracias por el entusiasmo, pero el problema es mucho más profundo de lo que indican sus preguntas. El módulo está escrito en C utilizando los enfoques estándar de Python para hacerlo. Comience en esta página para tener una idea: http://docs.python.org/extending/extending.html Las "herramientas" son mecanismos estándar de Python para las extensiones C, lo que intento lograr es la descripción, todas las plataformas, la biblioteca estándar no es relevante (el código está en C, no en Python), el módulo es mío y de código abierto y suficientemente complejo como para recomendar el uso del módulo de ejemplo de la página python doc. –

+0

¿Alguna vez ha producido un ejecutable congelado en Python? Dependiendo de la plataforma, se combina con todo el código ejecutable que necesita para ejecutarse. Una vez porté un programa de Python a Linux, y tuve que enviar libglib y libz a lo largo del ejecutable congelado. En Windows, OTOH hay msvcrtxx.dll que es posible que deba enviar también. Incluso si escribe el programa C con Python.lib vinculado de forma estática, deberá adjuntar bibliotecas dinámicas, lo que frustra el propósito de un solo archivo. – janislaw

Respuesta

2

-m implementación está en runpy._run_module_as_main. Su esencia es:

mod_name, loader, code, fname = _get_module_details(mod_name) 
<...> 
exec code in run_globals 

Un módulo compilado no tiene "código objeto" accociated con él por lo que la primera declaración falla con ImportError("No code object available for <module>"). Necesita extender runpy - específicamente, _get_module_details - para que funcione para un módulo compilado. Sugiero devolver un objeto de código construido a partir de la mencionada "import mod; mod.main()": (Python 2.6.1)

code = loader.get_code(mod_name) 
    if code is None: 
+  if loader.etc[2]==imp.C_EXTENSION: 
+   code=compile("import %(mod)s; %(mod)s.main()"%{'mod':mod_name},"<extension loader wrapper>","exec") 
+  else: 
+   raise ImportError("No code object available for %s" % mod_name) 
-  raise ImportError("No code object available for %s" % mod_name) 
    filename = _get_filename(loader, mod_name) 

(Actualización: fija un error en la cadena de formato)

Ahora ...

C:\Documents and Settings\Пользователь>python -m pythoncom 

C:\Documents and Settings\Пользователь> 

Esto todavía no funcionará para los módulos integrados. De nuevo, tendrá que inventar alguna noción de "unidad de código principal" para ellos.

Actualización:

He mirado a través de los llamados internos de _get_module_details y puedo decir con confianza que lo hacen ni siquiera intento para recuperar un objeto de código de un módulo de tipo distinto a imp.PY_SOURCE, o imp.PY_COMPILEDimp.PKG_DIRECTORY. Entonces tiene para parchar esta maquinaria de esta manera u otra para -m para que funcione. Python falla antes de recuperando algo de su módulo (ni siquiera comprueba si el dll es un módulo válido) por lo que no puede hacer construyéndolo de una manera especial.

+0

Gracias por la respuesta detallada. Esperaba poder realizar algún truco en mi código C que devuelva el módulo adjuntando un objeto de código allí. –

+0

Desde POV de administración, un script de contenedor me parece la mejor solución. Es fácil nombrarlo y colocarlo para que se llame en lugar de python.exe cada vez que un usuario invoca "python". –

+0

El problema con cualquier cosa que no sea 'python -m module' es que tengo que distribuir más de un archivo, y luego el usuario tiene que averiguar dónde colocar ese segundo archivo y poder ejecutarlo. Considere diferentes permisos (usuario versus administrador), diferentes plataformas, una distinción entre desarrolladores y usuarios para mi módulo, etc. Es una lástima que Python no permita que -m funcione con extensiones C y ni siquiera hay un truco desagradable que hará el truco –

-1

Creo que debe comenzar creando un archivo separado en Python y obteniendo la opción -m para que funcione. Luego, convierta ese archivo de Python en un objeto de código e incorpórelo a su archivo binario de tal manera que continúe funcionando.

Busque las herramientas de configuración en PyPi, descargue el .egg y eche un vistazo al archivo. Verá que los primeros bytes contienen una secuencia de comandos de Python y estos son seguidos por un archivo .ZIP bytestream. Algo similar puede funcionar para usted.

+1

Obtener el bytecode de Python es trivial: Py_CompileString lo obtendrá. Sin embargo, no veo forma de adjuntar el bytecode al PyObject devuelto por Py_InitModule3/PyModule_Create. –

-1

Hay una cosa completamente nueva que puede resolver sus problemas fácilmente. Acabo de enterarme y me parece decente: http://code.google.com/p/pts-mini-gpl/wiki/StaticPython

+1

No aplicable de forma remota. Quiero que mi módulo funcione con el Python instalado existente que no tiene uno construido por separado. –

0

¿Su requerimiento de binario único distribuido permite el uso de un huevo? Si es así, se puede empaquetar su módulo con un __main__.py con su código de llamada y la habitual __init__.py ...

Si usted es realmente inflexible, tal vez se podría extender pkgutil.ImpLoader.get_code devolver algo para los módulos C (por ejemplo, tal vez una función especial __code__). Para hacer eso, creo que vas a tener que cambiarlo realmente en la fuente de Python. Incluso entonces, pkgutil usa exec para ejecutar el bloque de código, por lo que tendría que ser el código de Python de todos modos.

TL; DR: Creo que estás euchred. Mientras que los módulos de Python tienen código a nivel global que se ejecuta en el momento de la importación, los módulos C no lo hacen; en su mayoría son solo un espacio de nombres dict. Por lo tanto, ejecutar un módulo C realmente no tiene sentido desde un punto de vista conceptual. Necesitas un código Python real para dirigir la acción.

+0

No creo que los huevos admitan el código compilado. Solo pude extender pkgutil mientras mi módulo se carga, ya que es el primero en que se ejecuta el código, pero sería demasiado tarde. Es realmente irritante que actualmente la solución sea 'python -c" import module; module.main() "'o que tenga un archivo .py con solo esas 4 palabras. –

Cuestiones relacionadas