11

I have a local directory named "calendar" with an "__init__.py" file.

I want "import calendar" to import the standard library module calendar, and not the module defined by my local directory.

I already tried "from __future__ import absolute_import" and changing PYTHONPATH.

There are Good Reasons I can't just rename the directory. Really.

Jonathan Paulson
  • 1,048
  • 6
  • 15
  • You can `import sys` and rearrange `sys.path` yourself. However, if you're saying you actually have a directory on `sys.path` that has a package/module named the same as a builtin one (i.e., your `calendar` is at the top level on the path), you really need to rename it (there are even better reasons, really). There's no general way to resolve top-level name conflicts. – BrenBarn Jan 02 '14 at 23:24
  • 1
    possible duplicate: http://stackoverflow.com/questions/6031584/python-importing-from-builtin-library-when-module-with-same-name-exists –  Jul 24 '14 at 12:58

3 Answers3

17

The problem is that the current working directory (as either '' or '.', depending on version/platform) is always at the top of sys.path when you start up Python.

Using absolute imports makes no difference—that just means to look in sys.path first, instead of looking for relative imports before falling back to sys.path.

The right solution is obviously to either (a) rename calendar, or (b) move it into subpackage of some other package instead of having it at the top level. Whatever your Good Reasons are, the Good Reasons for doing the right thing are likely even better.


But if you must get around this, there are a few things you can do. The simplest is to temporarily munge sys.path:

syspath = sys.path
sys.path = [path for path in sys.path if path.strip('.')]
import calendar
sys.path = syspath

However, no matter what you do, this is going to cause huge problems. When you later try to import your local package calendar—even if you're doing so from a completely different source file—nothing will happen, because there's already something named calendar in sys.modules, so that other source file will just get the stdlib calendar module instead of your package.

So you'll also need to rename one or the other on the fly and remove it from sys.modules. Maybe this:

syspath = sys.path
sys.path = [path for path in sys.path if path.strip('.')]
calmod = sys.modules.get('calendar')
del sys.modules['calendar']
calendar = __import__('calendar')
sys.modules['calendar'] = calmod
sys.path = syspath

And, depending on the order at which your modules get run (which may not be easily predictable, or even deterministic), there's a good chance you'll need similar hackery in the other location.

(What if you never actually need to import your local package calendar? Well, in that case you don't have this problem… but then I can't imagine what your Good Reasons could possibly be…)

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • 9
    I'm pretty disappointed that there isn't an explicit way to say "import from the standard library", or even just modify PYTHONPATH to say "import from the place where I installed Python first". Thanks for the details! – Jonathan Paulson Jan 02 '14 at 23:39
  • 1
    @JonathanPaulson: But you can't have two modules with the same qualified name—and that's global within your entire program—so such a language feature would just make it easier to accidentally trigger that ambiguity. If that isn't clear from the second half of my answer, my explanation probably sucks, so please let me know what you're not getting and I'll try again. – abarnert Jan 03 '14 at 00:03
  • Now I am disappointed that the internal fully-qualified name for a module isn't prefixed with the element of sys.path it came from. – Jonathan Paulson Jan 03 '14 at 01:18
  • 1
    @JonathanPaulson: That would be a huge mess. The `sys.path` generally has your system-wide site-packages, your distro's dist-packages, your per-user site-packages, a directory for each `pip`-installed package, an eggfile for each `easy_install`-installed package, … And I can't see how it would help anything. If those names are internal only, there's no way to specify which of the two you wanted, so what's the use of having both? If they're accessible, then you'd end up having to write different code depending on whether, say, `bs4` was installed as a dist, sys-site, user-site, etc. package… – abarnert Jan 03 '14 at 01:56
  • 2
    @JonathanPaulson: There _was_ a proposal for 3.0 to get rid of all built-in top-level packages and move everything into one giant `std` package (much as C++98 moved the whole stdlib into a `std::` namespace), which would have solved this problem… but if you can't imagine all the reasons people wouldn't want that, you can search for the long argument threads… – abarnert Jan 03 '14 at 01:58
  • How does Python make e.g. sure that standard library modules that import other standard library modules, really use those, and not just a local one? Imagine one imports `pathlib` (from the standard library), which in turn does `import os` (i.e. no relative import). If now locally there is a `os.py`, that would be taken. If you say just don't use those names, fine, but what if new ones are later added to the standard lib? – calestyo Jul 09 '23 at 01:06
3

You could modify sys.path, import the package, then restore sys.path to its original value.

import sys
original_path = sys.path
sys.path = original_path[1:]
import calendar
sys.path = original_path
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
2

As the rest says, the best option is to rename the calendar module into a new module not used by standard library.

But as a workaround if that's not possible, you could create a another module (visible in the syspath) outside the directory where the local calendar.py file is placed.

So if you have a hierarchy like this:

project/
   __init__.py
   app1/
      __init__.py
      calendar.py
      module_in_which_i_want_to_use_python_std_calendar.py

You could create a new module named std_calendar.py outside apps (outside calendar.py is placed). In this file you could import calendar (this will be standard calendar module)

from calendar import *

And the hierarchy would be:

project/
   __init__.py
   std_calendar.py
   app1/
      __init__.py
      calendar.py
      module_in_which_i_want_to_use_python_std_calendar.py

In module_in_which_i_want_to_use_python_std_calendar.py you could use standard calendar with:

from project import std_calendar as calendar
Iván Alegre
  • 1,818
  • 1
  • 14
  • 12