Importe la función local desde un módulo alojado en otro directorio con importaciones relativas en una notebook jupyter usando python3

Tengo una estructura de directorios similar a la siguiente

meta_project project1 __init__.py lib module.py __init__.py notebook_folder notebook.jpynb 

Cuando trabajo en notebook.jpynb si trato de usar una importación relativa para acceder a una función function() en module.py con:

 from ..project1.lib.module import function 

Obtuve el siguiente error

 SystemError Traceback (most recent call last)  in () ----> 1 from ..project1.lib.module import function SystemError: Parent module '' not loaded, cannot perform relative import 

¿Hay alguna manera de hacer que esto funcione con importaciones relativas?

Tenga en cuenta que el servidor de notebook se meta_project instancia en el nivel del directorio meta_project , por lo que debería tener acceso a la información en esos archivos.

Tenga en cuenta, además, que al menos como originalmente project1 no se pensó como un módulo y, por lo tanto, no tiene un archivo __init__.py , solo se __init__.py como un directorio del sistema de archivos. Si la solución al problema requiere tratarlo como un módulo e incluir un archivo __init__.py (incluso uno en blanco), está bien, pero hacerlo no es suficiente para resolver el problema.

Comparto este directorio entre las máquinas y las importaciones relativas me permiten usar el mismo código en todas partes, y a menudo utilizo cuadernos para realizar prototipos rápidamente, por lo que las sugerencias que involucran el hackeo de rutas absolutas no serán útiles.


Edición: esto es diferente a las Importaciones relativas en Python 3 , que se refiere a las importaciones relativas en Python 3 en general y, en particular, a ejecutar un script desde un directorio de paquetes. Esto tiene que ver con trabajar dentro de una notebook jupyter tratando de llamar a una función en un módulo local en otro directorio que tiene aspectos generales y particulares diferentes.

Tuve casi el mismo ejemplo que usted en este cuaderno en el que quería ilustrar el uso de la función de un módulo adyacente de forma SECA.

Mi solución fue decirle a Python de la ruta de importación de ese módulo adicional agregando un fragmento de código como este al cuaderno:

 import os import sys module_path = os.path.abspath(os.path.join('..')) if module_path not in sys.path: sys.path.append(module_path) 

Esto le permite importar la función deseada desde la jerarquía de módulos:

 from project1.lib.module import function # use the function normally function(...) 

Tenga en cuenta que es necesario agregar archivos __init__.py vacíos a __init__.py carpetas project1 / y lib / si todavía no los tiene.

Llegué aquí buscando las mejores prácticas para resumir el código a los submódulos cuando se trabaja en Notebooks. No estoy seguro de que haya una mejor práctica. He estado proponiendo esto.

Una jerarquía de proyectos como tal:

 ├── ipynb │  ├── 20170609-Examine_Database_Requirements.ipynb │  └── 20170609-Initial_Database_Connection.ipynb └── lib ├── __init__.py └── postgres.py 

Y desde 20170609-Initial_Database_Connection.ipynb :

  In [1]: cd .. In [2]: from lib.postgres import database_connection 

Esto funciona porque, de forma predeterminada, el Jupyter Notebook puede analizar el comando cd . Tenga en cuenta que esto no hace uso de la magia de Python Notebook. Simplemente funciona sin anteponer %bash .

Teniendo en cuenta que 99 veces de cada 100 estoy trabajando en Docker utilizando una de las imágenes de Project Jupyter Docker , la siguiente modificación es idempotente

  In [1]: cd /home/jovyan In [2]: from lib.postgres import database_connection 

Hasta ahora, la respuesta aceptada ha funcionado mejor para mí. Sin embargo, mi preocupación siempre ha sido que hay un escenario probable en el que podría refactorizar el directorio de los notebooks en subdirectorios, lo que requiere cambiar la module_path de module_path en cada cuaderno. Decidí agregar un archivo python dentro de cada directorio de notebook para importar los módulos requeridos.

Así, teniendo la siguiente estructura de proyecto:

 project |__notebooks |__explore |__ notebook1.ipynb |__ notebook2.ipynb |__ project_path.py |__ explain |__notebook1.ipynb |__project_path.py |__lib |__ __init__.py |__ module.py 

project_path.py el archivo project_path.py en cada subdirectorio de notebooks/explore ( notebooks/explore y notebooks/explain ). Este archivo contiene el código para las importaciones relativas (de @metakermit):

 import sys import os module_path = os.path.abspath(os.path.join(os.pardir, os.pardir)) if module_path not in sys.path: sys.path.append(module_path) 

De esta manera, solo necesito hacer importaciones relativas dentro del archivo project_path.py , y no en los cuadernos. Los archivos de las notebooks solo necesitarían importar project_path antes de importar lib . Por ejemplo en 0.0-notebook.ipynb :

 import project_path import lib 

La advertencia aquí es que revertir las importaciones no funcionaría. ESTO NO FUNCIONA:

 import lib import project_path 

Por lo tanto, se debe tener cuidado durante las importaciones.