Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I alias a python module at packaging time?

For historic reasons and backwards compatibility I want to package a module (let's call it myapi) in a way that this set of imports:

from myapi.utils import helpers
from myapi.api.rest import MyRest
import myapi

Import the same code (without duplication) as these below:

from oldname.utils import helpers
from oldname.api.rest import MyRest
import oldname

Assuming the package was installed the regular way with pip install myapi or pip install --user myapi.

I.e., I want to alias the complete module structure of myapi under another name oldname at packaging, so the user can import any module using oldname or the new name (here myapi) without duplicating the code. Is there something in Python or setuptools that can accomplish that?

Alternatives, that don't work for me:

I'm aware that the user could just do:

import myapi as oldname

My goal is to avoid confusion, because the package name changed at some point. The users should be able to use both the old and the new name interchangeably, without having to know the name changed at all.

On some systems, the easiest way would be to just create a symbolic link upon running setup.py install:

ln -s /usr/local/lib/python2.7/dist-packages/myapi \
      /usr/local/lib/python2.7/dist-packages/oldname

But that is hacky and will most certainly confuse Python's import system and lead to other problems (oldname.__name__, pip show oldname and such). Also it won't work with wheels. I prefer a built-in way but am not that deep into Python packaging that I would know one. Maybe there is a something I could put in my setup.py (which btw. uses setuptools). Any suggestions? Is there a better way?

like image 554
JCGB Avatar asked Jun 12 '19 10:06

JCGB


1 Answers

What I ended up using is the following package strucutre:

$ tree
./
├── myapi/
│   ├── utils/
│   │   ├── ...
│   │   └── __init__.py
│   └── api/
│       ├── rest.py
│       ├── ...
│       └── __init__.py
├── oldname/
│   └── __init__.py
└── setup.py

Where these are the relevent parts of the files:

# ./setup.py
from setuptools import setup, find_packages

setup(
    # ...
    packages=find_packages('./'),
    package_dir={'': './'},
)
# ./oldname/__init__.py

import sys, myapi
from myapi import *

sys.modules['oldname'] = myapi

Seems to work, but no idea if there are any edge-cases or whether there is a better solution and/or more canonical solution.

like image 123
JCGB Avatar answered Sep 21 '22 11:09

JCGB