Package: repackage
Package: Repackage==================Laurent Franceschetti March/June 2013 - 2018 MIT License.<!-- To update, run the following command: markdown-toc -i README.md --><!-- toc -->- [Purpose](#purpose)- [Install](#install)- [The problem](#the-problem)- [The solution](#the-solution)- [Usage](#usage)- [Importing a module using the system PATH](#importing-a-module-using-the-system-path)- [Limitations](#limitations)<!-- tocstop -->Purpose-------This module allows any Python program to call a non-registered packagein a reliable way. With this module, you may call "non-official"repositories, including with relative paths.> **CAUTION:** *This form is an alternative to system of relative pathsfor python imports ([PEP328](<https://www.python.org/dev/peps/pep-0328/#rationale-for-relative-imports>)). It allows additional flexibility, at the cost of breaking someassumptions.*Install-------If you are using pip: :pip install repackageOtherwise, download the package and type:```bashpython setup.py install```The problem-----------In Python, registered packages are called by name in importinstructions, and lower directories may be treated for all purposes aspackages.Two practical problems arise: a) How to easily call unregisteredpackages which have been dumped in an adjacent directory? b) How toeasily move python files into a sub-directory without messing up theimport statements?There are complicated issues with relative imports (see PEP366). Thebasic idea here is to add the source directory of the package to the libpath (thanks to a call to sys.path.append).But the probem, is how to programmatically find the source directory,from a relative path?Two often advocated methods to determine the path are: a. from currentdirectory or b. from \_\_FILE\_\_ .Both those methods have a flaw:-   The first does not take into account the file where the import ismade, hence will fail if the project is using more than onedirectory.-   The second does not allow to delegate those operations to a modulethat would handle those issues (as \_\_FILE\_\_ is going to pointnow to point to the module and not the caller).The solution------------This package uses a simple strategy that is likely to work in a goodrange of cases: it inspects the stack to determine which file is thecaller and works out the relative path from there. The only delicatepart consisted in working out how many steps down the stack this is, butthe answer should be invariant and can be computed both by reasoning andby trial and error (in this case: 3).Usage-----Situation 1) Moving the files into a lower directory. From the moduleyou want to make the call, just use the following statement before theimports: :import repackagerepackage.up()It should work without changing the imports that were previouslypointing to the upper directory.If it's two directories up, write: :repackage.up(2)Situation 2) Calling a non-registered directory somewhere else (absoluteor relative path): :repackage.add("../../otherdir")Clearly, repackage.up() would be equivalent to repackage.add("..") . Iprefer the first because it is more terse and syntactically more robust.Importing a module using the system PATH--------------------------In some cases, it is convenient to install a Python app as acommand in the PATH, usually with a symlink(e.g. `foo` => `/my/path/foo.py`).This allows us to execute it, regardless ofwhere it is installed.The idea behind Python packages is more or less the same.> But what ifthe module is **not** part of a package? Wouldn't it be intuitiveto exploit the PATH of the OS to import it?**This is possible.** Supposing `foo`is in the path, as a symlink to`my/path/foo.py`, then you can write:```Pythonrepackage.add_path("foo")import foo```Repackage will follow the symlinks all the way to the destinationand add the proper directory to your libpath. Needless to see, itwould work also if there is no symlink (note that in this case,the file in your path might be called 'foo.py')Limitations-----------If at some points in the execution, you attempt to add several times thesame directory to the lib path, this should remain without effect (thisis a feature of sys.path.append).This module has worked reliably for a while, so it is a beta version.The method seems robust so far, but not all ins and outs have beenexplored. One precaution might be to ensure that the repackaging alwayspoints to the same source directory of a package (not to subdirectoriesof the same package), so as to avoid possible ambiguities in the libpath. (If this really turned out to be a problem, this could be checkedon the fly and a warning issued?).If you find bugs, or even find this approach useless, essentially flawedor against the Zen of Python, I will be glad to hear about it.Similarly, if you liked it or have ideas on how to improve it, let meknow.