Skip to content

9. Las importaciones

El error encontrado en el version 1 del ejercicio de aplicación nos lleva a profundizar en la función de la instrucción [import].

Image

9.1. Scripts [import_01]

El script [imported] será importado por diferentes scripts (también llamados módulos):

Image


# módulo importado
# esta instrucción se ejecutará cada vez que se importe el módulo
print("2")
# variable perteneciente al módulo importado
x=4

Un módulo se ejecuta cuando se importa. Así, cuando se importe el módulo [imported]:

  • se mostrará la línea 3;
  • la variable x de la línea 5 recibirá su valor;

El script [main_01] es el siguiente:


# se ejecuta un módulo importado
import imported
# uso de la variable x del módulo importado
print(imported.x)
  • línea 2: se importa el módulo [imported]. Esto provocará su ejecución:
    • se mostrará el valor 2;
    • se crea la variable x con el valor 4;
  • línea 4: se utiliza la variable x del módulo importado;

En PyCharm, se señala un error:

En [1], PyCharm indica que no reconoce el módulo [imported]. En términos técnicos, esto significa que la carpeta que contiene el módulo [imported] no se encuentra en el Python Path de PyCharm. El Python Path es el conjunto de carpetas en las que se buscan los módulos importados. Para resolver este problema, basta con declarar [Sources root] como la carpeta en la que se encuentra el módulo [imported], en este caso la carpeta [import/01]:

Image

Image

Tras esta operación, la carpeta [import/01] se incluye en el Python Path de Pycharm y el error desaparece:

Image

  • en [1], la carpeta [01] ha cambiado de color;
  • en [2-3], ya no hay ningún error;

Los resultados de la ejecución son los siguientes:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_01.py
2
4

Process finished with exit code 0

Comentarios

  • la línea 2 es el resultado de la ejecución del módulo importado;
  • la línea 3 muestra el valor de la variable x del módulo importado;

De este ejemplo se desprende el importante concepto de que un módulo (o un script) importado se ejecuta.

El script [main_02] es el siguiente:


# se importa la variable x del módulo importado
from imported import x
# se muestra
print(x)
  • En la línea 2, tenemos otra sintaxis de importación: [from module import objet1, objet2, …]. Aquí se importa la variable [imported.x]. Con esta sintaxis, la variable x se convierte en una variable del script [main_02]. Ya no es necesario anteponerle el prefijo de su módulo [imported];
  • línea 4: se muestra la variable x de [main_02];

Los resultados de la ejecución son los siguientes:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_02.py
2
4

Process finished with exit code 0

El script [main_03] es el siguiente:


# se importa todo lo que es visible del módulo importado
from imported import *
# se utiliza la variable x del módulo importado
print(x)

La notación [import *] de la línea 2 significa que se importan todos los objetos visibles del módulo importado (variables, funciones).

Los resultados son los siguientes:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_03.py
2
4

Process finished with exit code 0

El script [main_04] es el siguiente:


# se importa la variable x del módulo importado
# y se renombra como y
from imported import x as y
# se muestra la variable y
print(y) 

La línea 3 muestra que se puede importar un objeto del módulo importado y asignarle un alias. Aquí, la variable [imported.x] se convierte en la variable [main_04.y]. Los resultados son los mismos que antes.

9.2. Script [import_02]

Image

El módulo importado [module1.py] es el siguiente:


# una función
def f1():
    print("f1")

El módulo importado define una función, algo muy habitual.

El script [main_01] es el siguiente:


# import
import module1
# ejecución f1
module1.f1()
  • línea 2, se importa el módulo. Se va a ejecutar. Aquí no muestra nada;
  • línea 4: se ejecuta la función [f1] del módulo importado;

Los resultados de la ejecución son los siguientes:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/02/main_01.py
f1

Process finished with exit code 0

Nota: Para evitar que PyCharm señale un error en la importación de la línea 2, hay que colocar la carpeta que contiene [module1] dentro de [Sources Root] de PyCharm:

Image

En [1], la carpeta [02], que se encuentra dentro de [Sources Root], aparece en azul. Cabe señalar que el error señalado no impide aquí la correcta ejecución de los scripts. De hecho, al ejecutar el script [main_0x], la carpeta del script se coloca automáticamente en el Python Path. De este modo, se encuentra [module1]. A partir de ahora, cuando en una captura de pantalla una carpeta aparece en azul, significa que se ha colocado en los [Sources Root] de PyCharm.

El script [main_02] es el siguiente:


# importar
from module1 import f1
# ejecución f1
f1()
  • la línea 2 importa la función [f1] del módulo [module1];
  • en la línea 4, se utiliza la función f1;

Los resultados son idénticos a los del script [main_01].

9.3. Scripts [import_03]

Image

Nota: [03] se encuentra en los [Sources Root] del proyecto.

Los nuevos scripts importarán el módulo [module2], que no se encuentra en la misma carpeta que ellos.

El script [module2] es el siguiente:


# una función
def f2():
    print("f2")

Por lo tanto, el script define una función [f2].

El script [main_01] es el siguiente:


# importación del módulo Class2
import dir1.module2
# ejecución f2
dir1.module2.f2()
  • línea 2: se utiliza una notación especial para indicar cómo encontrar el módulo [module2]. Hay que leer [dir1.module2] como la ruta [dir1/module2]: para encontrar [module2], se parte de la carpeta del script actual [main_01], luego se pasa a [dir1] y allí se encuentra [module2]. No hay que olvidar aquí que el punto de partida de la ruta es la carpeta del script que importa;
  • línea 4: para ejecutar la función [f2] desde [module2];

Los resultados son los siguientes:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/03/main_01.py
f2

Process finished with exit code 0

Línea 2, el resultado de la función [f2].

El script [main_02] es el siguiente:


# importación del módulo dir1.module2, al que se le cambia el nombre
import dir1.module2 as module2
# ejecución de f2
module2.f2()

Línea 2: se renombra el módulo [dir1.module] para simplificar la escritura de la línea 4.

El script [main_03] es el siguiente:


# importación de la función f2 del módulo dir1.module2
from dir1.module2 import f2
# ejecución de f2
f2()

Esta vez, en la línea 2, solo se importa la función [f2], que pasa a ser una función del script [main_03] (línea 4).

Todos estos scripts funcionan tanto en el contexto PyCharm como en el de una consola de Python. La razón es que, en ambos casos, la carpeta del script ejecutado, en este caso la carpeta [03], forma parte del Python Path. Por lo tanto, se encuentra la carpeta [dir1/module2].

9.4. Scripts [import_04]

Image

Aquí, las carpetas [dir1] y [dir2] se han colocado en [Sources Root] del proyecto PyCharm.

El primer módulo importado es [module3]:


# una función
def f3():
    print("f3")

El segundo módulo importado es [module4]:


from module3 import f3

# una función
def f4():
    f3()
    print("f4")
  • En la línea 1, se importa la función [f3] desde [module3]. Aquí, [module3] es visible porque se ha colocado su carpeta [dir1] dentro de [Sources Root];
  • líneas 4-6: se define una función [f4] que llama a la función [f3] de [module3];

El script principal [main_01] es el siguiente:


# se importa el módulo 4
from module4 import f4
# ejecución f4
f4()
  • En la línea 2, se importa el módulo [module4]. Este es visible porque se ha colocado su carpeta [dir2] en [Sources Root] de PyCharm;
  • línea 4: ejecución de la función [f4] de [module4];

Los resultados de la ejecución de [main_01] en PyCharm son los siguientes:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/04/main_01.py
f3
f4

Process finished with exit code 0

Ahora, ejecutemos [main_01] en un terminal (consola) de Python:

Image

Los resultados son los siguientes:

1
2
3
4
5
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\04>python main_01.py
Traceback (most recent call last):
  File "main_01.py", line 2, in <module>
    from module4 import f4
ModuleNotFoundError: No module named 'module4'

¿Qué ha pasado? El terminal de Python no tiene conocimiento del Python Path ni de los [Sources Root] de PyCharm. Tiene su propio Python Path. En este, siempre se tiene la carpeta del script que se está ejecutando, en este caso el script [main_01]. Por lo tanto, conoce la carpeta [import/04]. En el script ejecutado, encuentra la línea:


from module4 import f4

El intérprete de Python busca [module4] en las carpetas de su Python Path. Sin embargo, [module4] no se encuentra en [import/04], que sí está en el Python Path, sino en [import/04/dir2], donde no se encuentra. De ahí el error.

Así pues, nos encontramos con un problema ya conocido: un script que se ejecuta correctamente en PyCharm puede fallar en el contexto de un terminal de Python. Se trata de un problema recurrente que tendremos que resolver.

9.5. Scripts [import_05]

Image

Nota: las carpetas [dir1] y [dir2] se colocan en el Python Path. Cabe señalar que ya existe un conflicto: [module3] y [module4] se encuentran en dos ubicaciones del Python Path de PyCharm:

  • en [import/04/dir1] y [import/05/dir1] para [module3];
  • en [import/04/dir2] y [import/05/dir2] para [module4];

Entonces se pueden extraer [import/04/dir1] y [import/04/dir2] de los [Sources Root] del proyecto PyCharm. Resulta que, en este caso, [import/05/dir1] es una copia de [import/04/dir1] (lo mismo ocurre con [dir2]) y, por lo tanto, no hay ningún problema. No obstante, cabe señalar que, dentro del propio PyCharm, hay que prestar atención a la lista de carpetas de [Sources Root] para evitar conflictos.

El script [main_01] queda así:


import sys
# se modifica el sys.path para incluir las carpetas
# que contienen las clases que se van a importar
sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")
# se importa el módulo 4
from module4 import f4
# ejecución f4
f4()

Estamos tratando de resolver el problema del Python Path. Queremos uno que funcione igual de bien en PyCharm que en un terminal de Python. Para ello, lo vamos a arreglar nosotros mismos.

  • Líneas 4-6: añadimos las carpetas [., ./dir1, ./dir2] en el Python Path. Para que esto funcione, es necesario que la carpeta actual en el momento de la ejecución sea la carpeta [import/05]. Esto será así en PyCharm, pero no necesariamente en un terminal de Python, como veremos;
  • línea 8: importamos [module4]. Tras lo que acabamos de hacer, debería encontrarse en [./dir2];

La ejecución en PyCharm da los siguientes resultados:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/main_01.py
f3
f4

Process finished with exit code 0

Ahora, en un terminal de Python:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\05>python main_01.py
f3
f4

Línea 1, el archivo de ejecución es [import/05].

Ahora subamos un nivel en el árbol de [import/05]:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\05>cd ..

(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 05/main_01.py
Traceback (most recent call last):
  File "05/main_01.py", line 8, in <module>
    from module4 import f4
ModuleNotFoundError: No module named 'module4'
  • Línea 2: cuando se ejecuta [main_01], ya no estamos en la carpeta [import/05], sino en [import]. Sin embargo, hemos escrito:

sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")

Esto añade a Python Path las carpetas [import, import/dir1, import/dir2], que no es en absoluto lo que queremos. Cabe señalar que añadir a Python Path carpetas que no existen (import/dir1, import/dir2) no provoca errores.

Hemos avanzado, pero no es suficiente. Hay que añadir al Python Path, no rutas relativas, sino rutas absolutas.

El script [main_02] es una variante de [main_01] que utiliza un archivo de configuración [config.json]:


{
  "dependencies": [
    "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/dir1",
    "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/dir2"
  ]
}

El valor de la clave [dependencies] es la lista de carpetas que se deben añadir al Python Path. Cabe destacar que aquí se han utilizado nombres absolutos y no relativos.

El script [main_02] utiliza el archivo [config.json] de la siguiente manera:


import codecs
import json
import sys

# archivo de configuración
config_filename="C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/config.json"
# lectura del archivo de configuración json
with codecs.open(config_filename, "r", "utf-8") as file:
    config = json.load(file)

# modificación del sistema path
for directory in config['dependencies']:
    sys.path.append(directory)

# se importa el módulo 4
from module4 import f4
# ejecución de F4
f4()

  • línea 6: fíjate en que se ha utilizado el nombre absoluto del archivo de configuración;
  • líneas 8-9: se lee el archivo de configuración. Se crea un diccionario [config] (línea 9) con su contenido;
  • líneas 11-13: se añaden los elementos de la matriz [config['dependencies']] al Python Path. Cabe señalar que, dado que se han introducido nombres absolutos de carpetas en [config.json], se añaden nombres absolutos en el Python Path;
  • línea 16: se importa [module4]. Deberíamos encontrarlo, ya que ahora [dir2] está en el Python Path;

La ejecución da los mismos resultados que para [main_02], salvo que el script sigue funcionando cuando la carpeta de ejecución ya no es [import/05]:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 05/main_02.py
f3
f4

Línea 1, la carpeta de ejecución es [import].

Hemos avanzado. Hemos visto:

  • que teníamos que crear nosotros mismos el Python Path;
  • que había que incluir en él los nombres absolutos de todas las carpetas que contienen los módulos importados por la aplicación;

Sin embargo, poner nombres absolutos en los scripts no es una solución. En cuanto el proyecto se traslada a otro sitio, deja de funcionar. Tenemos que encontrar otra cosa.

9.6. Scripts [import_06]

Image

Nota: las carpetas [06, dir1, dir2] se han colocado en las [Sources Root] del proyecto PyCharm. Las carpetas [dir1, dir2] son idénticas a las de los ejemplos anteriores.

El archivo [config.json] es el siguiente:


{
  "rootDir": "C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06",
  "relativeDependencies": [
    "dir1",
    "dir2"
  ],
  "absoluteDependencies": [
   ]
}

Introducimos dos tipos de rutas:

  • rutas absolutas, líneas 7-8;
  • rutas relativas, líneas 3-6. Son relativas a la raíz de la línea 2. Así, cuando el proyecto cambie de ubicación, solo habrá que modificar esta línea;

El script [utils.py] utiliza el archivo [config.json] y genera el archivo Python Path:


# importaciones
import codecs
import json
import os
import sys

# configuración de la aplicación
def config_app(config_filename: str) -> dict:
    # config_filename: nombre del archivo de configuración
    # se permite que se propaguen las excepciones

    # procesamiento del archivo de configuración
    with codecs.open(config_filename, "r""utf-8"as file:
        config = json.load(file)
    # se añaden las dependencias al sys.path
    rootDir = config['rootDir']
    # se añaden las dependencias relativas del proyecto a la ruta del sistema
    for directory in config['relativeDependencies']:
        # se añade la dependencia al principio de la ruta del sistema
        sys.path.insert(0f"{rootDir}/{directory}")
    # se añaden las dependencias absolutas del proyecto a la ruta del sistema
    for directory in config['absoluteDependencies']:
        # se añade la dependencia al principio de la ruta del sistema
        sys.path.insert(0, directory)
    # se genera el diccionario de configuración
    return config

# carpeta del script ejecutado
def get_scriptdir():
    return os.path.dirname(os.path.abspath(__file__))
  • línea 8: la función [config_app] recibe como parámetro el nombre del archivo de configuración;
  • líneas 12-14: el archivo de configuración se utiliza para crear el diccionario [config];
  • línea 20: [sys.path] es la lista de carpetas del Python Path;
  • líneas 17-20: las dependencias relativas del archivo de configuración se añaden al Python Path. Se añaden al principio de la tabla [sys.path], línea 20. De hecho, cuando Python busca un módulo, explora las carpetas de [sys.path] en orden. Sin embargo, en este documento, los módulos con los mismos nombres se encontrarán en carpetas diferentes de [sys.path]. Al colocar las dependencias de la aplicación al principio de la tabla [sys.path], nos aseguramos de que estas se explorarán antes que otras carpetas de [sys.path] que podrían contener módulos con los mismos nombres;
  • líneas 21-24: las dependencias absolutas del archivo de configuración se añaden al Python Path;
  • línea 26: se devuelve la configuración de la aplicación;
  • líneas 29-30: la función [get_scriptdir] devuelve el nombre absoluto de la carpeta del script que se está ejecutando (aquella en la que se encuentra la llamada a la función);

El script principal [main] es el siguiente:


# importaciones
import sys

from utils import config_app


def affiche_path(msg: str):
    # mensaje
    print(f"{msg}------------------------------")
    # sys.path
    for path in sys.path:
        print(path)


# main -------------
try:
    # sys.path está configurado
    affiche_path("avant....")
    config = config_app(f"{get_scriptdir()}/config.json")
    affiche_path("après....")
    # se importa el módulo 4
    from module4 import f4
    # ejecución f4
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • línea 4: se importa la función [config_app]. Cabe señalar que, dado que [utils] y [main] se encuentran en la misma carpeta, este [import] funciona siempre. De hecho, la carpeta del script principal se añade automáticamente al Python Path;
  • líneas 7-12: la función [affiche_path] muestra la lista de carpetas del Python Path;
  • línea 19: se configura la aplicación. Tenga en cuenta que se pasa a la función [config_app] el nombre absoluto del archivo de configuración. Tras esta instrucción, se ha reconstruido el Python Path;
  • línea 22: se importa [module4]. Gracias a la reconstrucción de Python Path, se encontrará este módulo;
  • línea 24: se ejecuta la función [f4];

En el contexto PyCharm, los resultados de la ejecución son los siguientes:


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/06/main.py
avant....------------------------------
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions\shared
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\v01\shared

C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
après....------------------------------
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir2
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir1
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions\shared
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\v01\shared
….
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
f3
f4
done

Process finished with exit code 0

Comentarios

  • líneas 2-13: el Python Path de PyCharm. En él se encuentran todas las carpetas incluidas en el [Sources Root] del proyecto;
  • líneas 14-29: el Python Path creado por la función [config_app]. En las líneas 15-16 se encuentran las dos dependencias que hemos añadido;
  • líneas 22-27: las carpetas del sistema del intérprete de Python que ha ejecutado el script;
  • líneas 28-29: la ejecución se desarrolla con normalidad;

Ahora, situémonos en el contexto que hasta ahora provocaba un error de ejecución:

  • nos situamos en un terminal de Python;
  • nos situamos en una carpeta distinta a la que contiene el script ejecutado;

(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 06/main.py
avant....------------------------------
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
après....------------------------------
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir2
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir1
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
f3
f4
done

Esta vez funciona (líneas 20-21). Cabe señalar que [sys.path] no contiene las mismas carpetas que cuando la ejecución se realiza en PyCharm.

9.7. Scripts [import_07]

Mejoramos la solución anterior de dos maneras:

  • sustituimos el archivo de configuración [config.json] por un script [config.py]. De hecho, el archivo jSON plantea un problema importante: no se puede comentar. El diccionario [config.json] puede sustituirse por un diccionario de Python, que tiene la ventaja de que se le pueden añadir comentarios;
  • utilizamos un módulo visible para todos los proyectos de Python de la máquina;

9.7.1. Instalación de un módulo de ámbito de máquina

Image

Arriba, creamos una carpeta [packages/myutils] en el proyecto PyCharm (los nombres no importan).

El script [myutils.py] es el siguiente:


# importaciones
import sys
import os


def set_syspath(absolute_dependencies: list):
    # absolute_dependencies: una lista de nombres absolutos de carpetas

    # se añaden al syspath las dependencias absolutas del proyecto
    for directory in absolute_dependencies:
        # se comprueba la existencia de la carpeta
        existe = os.path.exists(directory) and os.path.isdir(directory)
        if not existe:
            # se lanza una excepción
            raise BaseException(f"[set_syspath] le dossier du Python Path [{directory}] n'existe pas")
        else:
            # se añade la carpeta al principio de la ruta del sistema
            sys.path.insert(0, directory)
  • líneas 6-18: la función [set_syspath] crea un archivo Python Path con la lista de carpetas que se le pasan como parámetro;
  • líneas 12-15: se comprueba que existe la carpeta que se va a incluir en el Python Path;

El script [__init.py__] (dos guiones bajos delante y detrás del nombre; este es obligatorio) es el siguiente:


from .myutils import set_syspath

Se importa la función [set_syspath] del script [myutils]. La notación [.myutils] designa la ruta [./myutils], por lo que el script [myutils] se encuentra en la misma carpeta que [__init.py]. Se podría haber utilizado la notación [myutils]. Sin embargo, vamos a crear un módulo [myutils] de ámbito de máquina. Por lo tanto, la notación [from myutils import set_syspath] resultaría ambigua. ¿Se trata de importar el script [myutils] de la carpeta actual o el script [myutils] de ámbito de máquina? La notación [.myutils] elimina esta ambigüedad.

El script [setup.py] (también en este caso el nombre viene impuesto) es el siguiente:


from setuptools import setup

setup(name='myutils',
      version='0.1',
      description='Utilitaire fixant le Python Path',
      url='#',
      author='st',
      author_email='st@gmail.com',
      license='MIT',
      packages=['myutils'],
      zip_safe=False)

En este script se describe el módulo que se va a crear. En este caso, lo crearemos localmente. Pero se utiliza el mismo proceso para crear un módulo distribuido oficialmente (véase |pypi|). Los puntos importantes son los siguientes:

  • línea 3: el nombre del módulo creado;
  • línea 4: el version del módulo;
  • línea 5: su descripción;
  • líneas 7-8: el autor del módulo;

Para instalar este módulo con un ámbito de máquina, se procede de la siguiente manera:

Image

A continuación, en el terminal de Python, se escribe el siguiente comando [pip install .]:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\packages>pip install .
Processing c:\data\st-2020\dev\python\cours-2020\python3-flask-2020\packages
Using legacy setup.py install for myutils, since package 'wheel' is not installed.
Installing collected packages: myutils
  Attempting uninstall: myutils
    Found existing installation: myutils 0.1
    Uninstalling myutils-0.1:
      Successfully uninstalled myutils-0.1
    Running setup.py install for myutils ... done
Successfully installed myutils-0.1

A partir de ahora, cualquier script de la máquina puede importar el módulo [myutils] sin que este se encuentre en los códigos del proyecto.

9.7.2. El script [config.py]

Image

El script [config.py] se encarga de la configuración de la aplicación:


def configure():
    import os

    # nombre absoluto de la carpeta del script de configuración
    script_dir = os.path.dirname(os.path.abspath(__file__))
    # rutas absolutas de las carpetas que se deben incluir en el syspath
    absolute_dependencies = [
        # carpetas locales
        f"{script_dir}/dir1",
        f"{script_dir}/dir2",
    ]
    # actualización de syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    # se devuelve el config
    return {}
  • línea 1: la función [configure] se encarga de la configuración de la aplicación;
  • líneas 7-10: el diccionario que antes estaba en [config.json];
  • líneas 9-10: como estamos en un script, podemos obtener directamente los nombres absolutos de las carpetas [dir1, dir2];
  • líneas 12-14: utilizamos la función [set_syspath] del módulo [myutils] que acabamos de crear para definir el Python Path de la configuración;
  • línea 20: devolvemos el diccionario de la configuración de la aplicación. En este caso, está vacío;

9.7.3. El script [main.py]

El script principal [main] es el siguiente:


# se configura la aplicación
import config

config = config.configure()

# el syspath está configurado; ya se pueden realizar las importaciones
from module4 import f4

# main -------------
try:
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • líneas 2-4: se configura la aplicación mediante el módulo [config.py]. Se puede acceder a este porque se encuentra en la misma carpeta que el script principal. Ahora bien, la carpeta del script principal sigue formando parte de Python Path;
  • al llegar a la línea 6, el Python Path se ha creado con la carpeta del módulo [module4] en su interior. Por lo tanto, podemos importarlo en la línea 7;
  • líneas 10-15: solo queda ejecutar la función [f4];

Los resultados de la ejecución en PyCharm son los siguientes:

1
2
3
4
5
6
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/07/main.py
f3
f4
done

Process finished with exit code 0

En un terminal de Python y fuera de la carpeta del script principal, los resultados son los siguientes:

1
2
3
4
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 07/main.py
f3
f4
done

A partir de ahora, siempre seguiremos el mismo procedimiento para configurar una aplicación:

  • presencia de un script [config.py] en la carpeta del script principal. Este script contiene una función [configure] que tiene dos objetivos:
    • compilar el Python Path para la aplicación. Para ello, [config.py] declara todas las carpetas que contienen los módulos utilizados por la aplicación y compila el Python Path con sus nombres absolutos;
    • construir el diccionario [config] de la configuración de la aplicación;

Aplicamos este esquema a la segunda version del ejercicio de aplicación. Recordemos que la función version 1 funcionaba en el entorno PyCharm, pero no en un terminal de Python. El problema provenía del Python Path.