Namespaces without packages
I’ve implemented a comprehensive solution and published it as a package, ‘box’.
Internally, ‘box’ modules uses an approach similar to packages; that is, it loads the code inside a dedicated namespace environment and then exports selected symbols into a module environment which is returned to the user, and optionally attached. The main difference to packages is that modules are more lightweight and easier to write (each R file is its own module), and can be nested.
Usage of the package is described in detail on its website.
Should one avoid a Python module being named in its own namespace?
Too long for a comment; TLDR: try to avoid extra imports and show the code to get concrete suggestions.
- No, there is no established rationale for this. Do whatever makes sense. Having shorter imports is more user-friendly; cases when you have to introduce a no-op import layer on top are VERY RARE.
Note that from tqdm import tqdm
actually imports object tqdm
from module tqdm
, so it is not really an empty import layer on top - the module contains a bunch of other objects.
- Python project are diverse. In fact, many high profile projects are written in other languages, like C/C++. They all have different folder structure, so there is no one size fits all.
python namespaces vs packages: making a package the default namespace
You would add the 'old' names inside your new package by importing into the top-level package.
Names imported as globals in pypackage/__init__.py
are attributes on the pypackage
package. Make use of that to give access to 'legacy' locations:
# add all public names from pypackage.core.pypackage to the top level for
# legacy package use
from .core.pypackage import *
Now any code that uses import pypackage
can use pypackage.foo
and pypackage.bar
if in reality these objects were defined in pypackage.core.pypackage
instead.
Now, because pypackage
is a setuptools namespace package you have a different problem; namespace packages are there for multiple separate distributions to install into so that top-level package must either be empty or only contain a minimum __init__.py
file (namespace packages created with empty directories require Python 3.3).
If you are the only publisher of distributions that use this namespace, you can cheat a little here and use a single __init__.py
file in your core
package that could use pkg-util-style __init__.py
file with the additional import I used above, but then you must not use any __init__.py
files in other distribution packages or require that they all use the exact same __init__.py
content. Coordination is key here.
Or you would have to use a different approach. Leave pypackage
as a legacy wrapper module, and rename the new package format to use a new, different top-level name that can live next to the old module. At this point you can then just include the legacy package in your project, directly, as an extra top-level module.
Namespace vs regular package
Namespace packages
As of Python 3.3, we get namespace packages. These are a special kind of package that allows you to unify two packages with the same name at different points on your Python-path. For example, consider path1 and path2 as separate entries on your Python-path:
path1
+--namespace
+--module1.py
+--module2.py
path2
+--namespace
+--module3.py
+--module4.py
with this arrangement you should be able to do the following:
from namespace import module1, module3
thus you get the unification of two packages with the same name in a single namespace.
If either one of them gain an __init__.py
that becomes the package - and you no longer get the unification as the other directory is ignored.
If both of them have an __init__.py
, the first one in the PYTHONPATH (sys.path
) is the one used.
__init__.py
used to be required to make directory a package
Namespace packages are packages without the __init__.py
.
For an example of a simple package, if you have a directory:
root
+--package
+--file1.py
+--file2.py
...
While you could run these files independently in the package
directory, e.g. with python2 file1.py
, under Python 2 you wouldn't be able to import the files as modules in the root directory, e.g.
import package.file1
would fail, and in order for it to work, you at least need this:
package
+--__init__.py
+--file1.py
+--file2.py
...
__init__.py
initializes the package so you can have code in the __init__.py
that is run when the module is first imported:
run_initial_import_setup()
provide an __all__
list of names to be imported,
__all__ = ['star_import', 'only', 'these', 'names']
if the package is imported with the following:
from module import *
or you can leave the __init__.py
completely empty if you only want to be able to import the remaining .py files in the directory.
Namespaces with __init__.py
using pkgutil:
You could originally use pkgutil, available since Python 2.3. to accomplish adding namespaces, by adding the following into each separate package's __init__.py
:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
Setuptools uses a similar method, again, all __init__.py
files should contain the following (with no other code):
import pkg_resources
pkg_resources.declare_namespace(__name__)
Namespaces were more thoroughly addressed in PEP 420
See also more discussion on setuptools and Namespaces here:
http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
working with package without Namespace in R
Based on the discussion above I am answering my own questions. All credit goes to who discussed this issue.
Create a file with the following text and put an extra line to avoid an error. Same in the directory for the package where you have description file.
exportPattern("^[^\\.]")
How do I create a namespace package in Python?
TL;DR:
On Python 3.3 you don't have to do anything, just don't put any __init__.py
in your namespace package directories and it will just work. On pre-3.3, choose the pkgutil.extend_path()
solution over the pkg_resources.declare_namespace()
one, because it's future-proof and already compatible with implicit namespace packages.
Python 3.3 introduces implicit namespace packages, see PEP 420.
This means there are now three types of object that can be created by an import foo
:
- A module represented by a
foo.py
file - A regular package, represented by a directory
foo
containing an__init__.py
file - A namespace package, represented by one or more directories
foo
without any__init__.py
files
Packages are modules too, but here I mean "non-package module" when I say "module".
First it scans sys.path
for a module or regular package. If it succeeds, it stops searching and creates and initalizes the module or package. If it found no module or regular package, but it found at least one directory, it creates and initializes a namespace package.
Modules and regular packages have __file__
set to the .py
file they were created from. Regular and namespace packages have __path__
set to the directory or directories they were created from.
When you do import foo.bar
, the above search happens first for foo
, then if a package was found, the search for bar
is done with foo.__path__
as the search path instead of sys.path
. If foo.bar
is found, foo
and foo.bar
are created and initialized.
So how do regular packages and namespace packages mix? Normally they don't, but the old pkgutil
explicit namespace package method has been extended to include implicit namespace packages.
If you have an existing regular package that has an __init__.py
like this:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
... the legacy behavior is to add any other regular packages on the searched path to its __path__
. But in Python 3.3, it also adds namespace packages.
So you can have the following directory structure:
├── path1
│ └── package
│ ├── __init__.py
│ └── foo.py
├── path2
│ └── package
│ └── bar.py
└── path3
└── package
├── __init__.py
└── baz.py
... and as long as the two __init__.py
have the extend_path
lines (and path1
, path2
and path3
are in your sys.path
) import package.foo
, import package.bar
and import package.baz
will all work.
pkg_resources.declare_namespace(__name__)
has not been updated to include implicit namespace packages.
Related Topics
How to Write from R to the Clipboard on a MAC
Centering Image and Text in R Markdown for a PDF Report
How to Plot Logit and Probit in Ggplot2
How to Find the Polygon Nearest to a Point in R
Ggplot Legend Issue W/ Geom_Point and Geom_Text
Count Consecutive Numbers in a Vector
R- Converting Data from Fraction to Decimal
Dynamically Add Function to R6 Class Instance
How to Avoid Using Round() in Every \Sexpr{}
R - Return Position of Element in Matrix
Differencebetween These Two Comparisons
How to Correctly Interpret Ggplot's Stat_Density2D
How to Change the Position of the Table of Contents in Rmarkdown