Etiqueta: python

  • Automatizar el reemplazo de un método en tests

    Método para automatizar el reemplazo de un método en el código a comprobar.

    class BaseTestEnvironment(unittest.IsolatedAsyncioTestCase):
        def _patch_method(self):
            async def new_method(arg_1: Any, *args: Any):
                return "anything"
    
            patcher = patch.object(
                TheClass,
                "the_method",
                side_effect=new_method,
            )
            self._m_method = patcher.start()
            self.addCleanup(patcher.stop)

    Llamando desde cualquier test a self._patch_method se realizará el patch del método TheClass.the_method durante la ejecución del test.

  • Mi VSCode settings.json

    Este es el contenido (en evolución) de mi fichero de configuración de VSCode. Está únicamente orientado a Python, ya que es lo único que utilizo ahora mismo. No está aun completo, hay tareas (como la inserción automática de los import necesarios) con las que aun no me siento lo suficientemente cómodo en VSCode.

    {
    "debug.console.fontSize": 14,
    "diffEditor.codeLens": true,"editor.fontSize": 14,
    "editor.minimap.showSlider": "always",
    "editor.rulers": [88],
    "editor.wordWrap": "wordWrapColumn",
    "editor.wordWrapColumn": 88,
    "editor.wrappingIndent": "indent",
    
    "gitlens.currentLine.enabled": false,
    "gitlens.hovers.currentLine.over": "line",
    
    "remote.SSH.defaultExtensions": [
        "gitpod.gitpod-remote-ssh"
    ],
    "remote.SSH.configFile": "/var/folders/qn/whatever/T/gitpod_ssh_config-more-whatever",
    
    "python.analysis.autoImportCompletions": true,
    "python.analysis.completeFunctionParens": true,
    "python.analysis.diagnosticSeverityOverrides": {
        "reportMissingTypeStubs": "information"
    },
    "python.analysis.indexing": true,
    "python.analysis.typeCheckingMode": "strict",
    "python.analysis.useLibraryCodeForTypes": true,
    "python.diagnostics.sourceMapsEnabled": true,
    "python.envFile": "${workspaceFolder}/.env",
    "python.formatting.autopep8Args": [
        "\"--max-line-length\", \"89\""
    ],
    "python.languageServer": "Pylance",
    "python.linting.banditEnabled": true,
    "python.linting.mypyEnabled": true,
    "python.terminal.activateEnvInCurrentTerminal": true,
    "python.testing.pytestEnabled": true,
    "python.testing.unittestEnabled": true,
    "vsintellicode.python.completionsEnabled": true,
    
    "scm.inputFontSize": 14,
    "terminal.integrated.fontSize": 14,
    "vim.textwidth": 89,
    "workbench.fontAliasing": "antialiased",
    "workbench.tree.indent": 16,
    }

  • Importar un módulo Python dinámicamente

    Para poder utilizar un mismo código Python que tenía duplicado con distintas configuraciones en distintos directorios pensé en poner las variables que definen la configuración del trabajo a realizar en ficheros .py particulares y cargarlos como un módulo desde el ahora único fichero de código con lógica. Pensé en esta chapuza de ficheros de configuración para ahorrarme trabajo y no tener que analizar archivos JSON, YAML o cualquier otro formato. Al final el trabajo me lo ahorré gracias a un experto en Python que trabaja conmigo.

    Para resumir digamos que tengo X ficheros Python del estilo de estos dos:

    #!/usr/bin/python
    #Configuración 1
    variable = "Por aquí"
    #Lógica
    print variable
    #!/usr/bin/python
    #Configuración 2
    variable = "Por allá"
    #Lógica
    print variable

    Lo que quería hacer es tener un fichero único con la lógica y un fichero particular por cada configuración importado desde el fichero de lógica. Así, los ficheros de configuración serían los siguientes:

    #!/usr/bin/python
    #Configuración 1
    variable = "Por aquì"
    #!/usr/bin/python
    #Configuración 2
    variable = "Por allá"

    Al fichero de lógica le proporcionaría la ruta del fichero de configuración a importar y listo. Pero en el fichero de lógica no se puede utilizar un simple import, ya que el nombre de los ficheros de configuración a importar es dinámico.

    Buscando por ahí llegué hasta el punto en que teniendo la ruta del fichero de configuración preparada, lograba (más o menos) importar el fichero de configuración utilizando el módulo importlib. Aclaro que el entorno en el que trabajo es Python 2.7, aunque creo, que esto funciona igual en Python 3. Con la documentación y los artículos que había leído llegué hasta este código que requiere como primer parámetro la ruta del directorio donde buscar el módulo y como segundo el nombre del módulo (nombre del fichero eliminando el sufijo .py):

    #!/usr/bin/python
    import os
    import sys
    # Obvio las comprobaciones de número de parámetros y existencia de rutas
    directorioConfiguracion = os.path.abspath(sys.argv[1])
    nombreModulo = sys.argv[2]

    # Aquí se le dice donde buscar el módulo
    sys.path.append(directorioConfiguracion)

    # Aquí trato (sin éxito aún) de importarlo correctamente
    import importlib
    importlib.import_module(nombreModulo)

    # Aquí se ve que el módulo se ha "cargado" con el nombre proporcionado como nombreModulo
    print sys.modules

    El módulo era encontrado y cargado, pero no veía la forma de poder acceder a las variables que había en él. Y, (muy) probablemente, seguiría sin verla mientras escribo esto de no haber consultado al mencionado experto compañero. Él, echando un vistazo me dijo: «¿no tendrías que asignar el resultado del import_module a una variable?». Ahora me parece obvio que debía ser así.

    Así, el código correcto resultó ser el siguiente:

    #!/usr/bin/python
    import os
    import sys
    # Obvio las comprobaciones de número de parámetros y existencia de rutas
    directorioConfiguracion = os.path.abspath(sys.argv[1])
    nombreModulo = sys.argv[2]

    # Aquí se le dice donde buscar el módulo
    sys.path.append(directorioConfiguracion)

    # Aquí trato (sin éxito aún) de importarlo correctamente
    import importlib
    moduloConfiguracion = importlib.import_module(nombreModulo)

    #Lógica
    print moduloConfiguracion.variable