sino significa que programacion operadores not else ejemplos condicional anidado python compatibility popen

significa - si anidado en python



¿Qué es lo más cerca que puedo de llamar a una función de Python usando una versión diferente de Python? (4)

Aquí hay una implementación de ejemplo completa usando subprocess y pickle que realmente probé. Tenga en cuenta que necesita usar la versión 2 del protocolo explícitamente para el decapado en el lado de Python 3 (al menos para el combo Python 3.5.2 y Python 2.7.3).

# py3bridge.py import sys import pickle import importlib import io import traceback import subprocess class Py3Wrapper(object): def __init__(self, mod_name, func_name): self.mod_name = mod_name self.func_name = func_name def __call__(self, *args, **kwargs): p = subprocess.Popen([''python3'', ''-m'', ''py3bridge'', self.mod_name, self.func_name], stdin=subprocess.PIPE, stdout=subprocess.PIPE) stdout, _ = p.communicate(pickle.dumps((args, kwargs))) data = pickle.loads(stdout) if data[''success'']: return data[''result''] else: raise Exception(data[''stacktrace'']) def main(): try: target_module = sys.argv[1] target_function = sys.argv[2] args, kwargs = pickle.load(sys.stdin.buffer) mod = importlib.import_module(target_module) func = getattr(mod, target_function) result = func(*args, **kwargs) data = dict(success=True, result=result) except Exception: st = io.StringIO() traceback.print_exc(file=st) data = dict(success=False, stacktrace=st.getvalue()) pickle.dump(data, sys.stdout.buffer, 2) if __name__ == ''__main__'': main()

El módulo Python 3 (utilizando el módulo pathlib para el escaparate)

# spam.py import pathlib def listdir(p): return [str(c) for c in pathlib.Path(p).iterdir()]

El módulo Python 2 usando spam.listdir

# beans.py import py3bridge delegate = py3bridge.Py3Wrapper(''spam'', ''listdir'') py3result = delegate(''.'') print py3result

Digamos que tengo dos archivos:

# spam.py import library_Python3_only as l3 def spam(x,y) return l3.bar(x).baz(y)

y

# beans.py import library_Python2_only as l2 ...

Ahora supongamos que deseo llamar spam desde dentro de beans . No es directamente posible ya que ambos archivos dependen de versiones incompatibles de Python. Por supuesto, puedo hacer que Popen un proceso de python diferente, pero ¿cómo podría pasar los argumentos y recuperar los resultados sin mucho dolor de análisis de flujo?


Es posible utilizar el módulo multiprocessing.managers para lograr lo que desea. Sin embargo, requiere una pequeña cantidad de piratería.

Dado un módulo que tiene funciones que desea exponer, necesita crear un Manager que pueda crear proxies para esas funciones.

Proceso de administrador que sirve proxies a las funciones py3:

from multiprocessing.managers import BaseManager import spam class SpamManager(BaseManager): pass # Register a way of getting the spam module. # You can use the exposed arg to control what is exposed. # By default only "public" functions (without a leading underscore) are exposed, # but can only ever expose functions or methods. SpamManager.register("get_spam", callable=(lambda: spam), exposed=["add", "sub"]) # specifying the address as localhost means the manager is only visible to # processes on this machine manager = SpamManager(address=(''localhost'', 50000), authkey=b''abc'', serializer=''xmlrpclib'') server = manager.get_server() server.serve_forever()

He redefinido el spam para que contenga dos funciones llamadas add y sub .

# spam.py def add(x, y): return x + y def sub(x, y): return x - y

Proceso de cliente que utiliza las funciones py3 expuestas por SpamManager .

from __future__ import print_function from multiprocessing.managers import BaseManager class SpamManager(BaseManager): pass SpamManager.register("get_spam") m = SpamManager(address=(''localhost'', 50000), authkey=b''abc'', serializer=''xmlrpclib'') m.connect() spam = m.get_spam() print("1 + 2 = ", spam.add(1, 2)) # prints 1 + 2 = 3 print("1 - 2 = ", spam.sub(1, 2)) # prints 1 - 2 = -1 spam.__name__ # Attribute Error -- spam is a module, but its __name__ attribute # is not exposed

Una vez configurado, este formulario proporciona una forma fácil de acceder a funciones y valores. También permite que estas funciones y valores se usen de una manera similar a la que podría usar si no fueran servidores proxy. Finalmente, le permite establecer una contraseña en el proceso del servidor para que solo los procesos autorizados puedan acceder al administrador. El hecho de que el administrador sea de larga duración también significa que no es necesario iniciar un nuevo proceso para cada llamada de función que realice.

Una limitación es que he usado el módulo xmlrpclib lugar de pickle para enviar datos entre el servidor y el cliente. Esto se debe a que python2 y python3 usan diferentes protocolos para pickle . Puede solucionar esto agregando su propio cliente a multiprocessing.managers.listener_client que usa un protocolo acordado para el decapado de objetos.


Podrías crear un script simple como tal:

import sys import my_wrapped_module import json params = sys.argv script = params.pop(0) function = params.pop(0) print(json.dumps(getattr(my_wrapped_module, function)(*params)))

Podrás llamarlo así:

pythonx.x wrapper.py myfunction param1 param2

Esto es obviamente un peligro para la seguridad, sin embargo, tenga cuidado

También tenga en cuenta que si sus parámetros son algo más que una cadena o enteros, tendrá algunos problemas, por lo que tal vez piense en transmitir parámetros como una cadena json y json.loads() utilizando json.loads() en el envoltorio.


Suponiendo que la persona que llama es Python3.5 +, tiene acceso a un módulo de subprocess más agradable. Tal vez podría subprocess.run y comunicarse a través de objetos Python encurtidos enviados a través de stdin y stdout, respectivamente. Debería haber alguna configuración que hacer, pero no hacer un análisis de su lado, o jugar con cuerdas, etc.

Aquí hay un ejemplo de código Python2 a través de subproceso.

p = subprocess.Popen(python3_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) stdout, stderr = p.communicate(pickle.dumps(python3_args)) result = pickle.load(stdout)