-1

There seem to be file names that are not allowed to exist in the same directory in which I'm running my Python source code because they probably conflict with a file of the same name within a module in which I've imported.

Is there a way to still keep the names the same but avoid the exception?

This seems bizarre being how long Python has been around and is not fixed or the default install of Python doesn't avoid this, despite what modules are imported (ie: openpyxl in the copy.py scenario & smtplib in the email.py scenario).

Below are two example exceptions. My source code is in the testing.py file. And files copy.py and email.py are in the same directory as testing.py. For now, I renamed them to copy2.py and email2.py, but this seems like a bad workaround.

copy.py

Traceback (most recent call last):
  File "C:\Python\Python37_64\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\Python\Python37_64\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Python\Python37_64\lib\cProfile.py", line 185, in <module>
    main()
  File "C:\Python\Python37_64\lib\cProfile.py", line 178, in main
    runctx(code, globs, None, options.outfile, options.sort)
  File "C:\Python\Python37_64\lib\cProfile.py", line 20, in runctx
    filename, sort)
  File "C:\Python\Python37_64\lib\profile.py", line 62, in runctx
    prof.runctx(statement, globals, locals)
  File "C:\Python\Python37_64\lib\cProfile.py", line 100, in runctx
    exec(cmd, globals, locals)
  File "C:\GitLab\redcap-p01-etl\testing.py", line 7, in <module>
    import openpyxl # openpyxl 3.0.7
  File "C:\Python\venv\Python37_64\lib\site-packages\openpyxl\__init__.py", line 6, in <module>
    from openpyxl.workbook import Workbook
  File "C:\Python\venv\Python37_64\lib\site-packages\openpyxl\workbook\__init__.py", line 4, in <module>
    from .workbook import Workbook
  File "C:\Python\venv\Python37_64\lib\site-packages\openpyxl\workbook\workbook.py", line 4, in <module>
    from copy import copy
  File "C:\GitLab\p01\copy.py", line 2
    def parse_slash_copy(const char *args):
    ^
IndentationError: unexpected indent

email.py

Traceback (most recent call last):
  File "C:\Python\Python37_64\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\Python\Python37_64\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Python\Python37_64\lib\cProfile.py", line 185, in <module>
    main()
  File "C:\Python\Python37_64\lib\cProfile.py", line 178, in main
    runctx(code, globs, None, options.outfile, options.sort)
  File "C:\Python\Python37_64\lib\cProfile.py", line 20, in runctx
    filename, sort)
  File "C:\Python\Python37_64\lib\profile.py", line 62, in runctx
    prof.runctx(statement, globals, locals)
  File "C:\Python\Python37_64\lib\cProfile.py", line 100, in runctx
    exec(cmd, globals, locals)
  File "C:\GitLab\p01\testing.py", line 13, in <module>
    import requests
  File "C:\Python\venv\Python37_64\lib\site-packages\requests\__init__.py", line 43, in <module>
    import urllib3
  File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\__init__.py", line 11, in <module>
    from . import exceptions
  File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\exceptions.py", line 3, in <module>
    from .packages.six.moves.http_client import IncompleteRead as httplib_IncompleteRead
  File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\packages\six.py", line 199, in load_module
    mod = mod._resolve()
  File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\packages\six.py", line 113, in _resolve
    return _import_module(self.mod)
  File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\packages\six.py", line 82, in _import_module
    __import__(name)
  File "C:\Python\Python37_64\lib\http\client.py", line 71, in <module>
    import email.parser
  File "C:\GitLab\p01\email.py", line 1, in <module>
    import smtplib
  File "C:\Python\Python37_64\lib\smtplib.py", line 47, in <module>
    import email.utils
ModuleNotFoundError: No module named 'email.utils'; 'email' is not a package
JustBeingHelpful
  • 18,332
  • 38
  • 160
  • 245
  • 1
    Your first example has nothing to do with the file name; you have a syntax error in the file. Your second one assumes that `email` is not just a module, but a *package*, and you cannot define a package with a single file. – chepner May 16 '22 at 17:24
  • 1
    @chepner The builtin `email` is a package. – MisterMiyagi May 16 '22 at 17:25
  • No names are "not allowed". But names have to *name* something, which means you can *shadow* existing names. It is *the same thing as* when you try to use a variable named `sum` in your program, and then find that you can no longer call the built-in `sum` function. In this case, the standard library wants to (very indirectly) import a package named `email`, but your locally defined module named `email` is found instead. – Karl Knechtel May 16 '22 at 17:27
  • However, email.py and copy.py are not connected at all to testing.py. I'm not calling any source code in those two files. – JustBeingHelpful May 16 '22 at 17:28
  • And, again, the first error message has **nothing to do with** `import`ing and **will not be fixed** by renaming the file. While it's true that your project's `copy.py` is conflicting with the one in the standard library, your project's `copy.py` *also* has a syntax error which must be fixed before the code in *that* project can work. – Karl Knechtel May 16 '22 at 17:29
  • Renaming DID fix the issue. I'm not using either of those two files. – JustBeingHelpful May 16 '22 at 17:30
  • "However, email.py and copy.py are not connected at all to testing.py. I'm not calling any source code in those two files." That's **not the issue**. They are being found *indirectly, by the libraries you are trying to use*, because they have the same names as *other standard library contents that are brought in indirectly*. – Karl Knechtel May 16 '22 at 17:31
  • "Renaming DID fix the issue" It will not fix the issue that you see when you try to run the `copy2.py`, or consciously use it for anything. – Karl Knechtel May 16 '22 at 17:32
  • I understand that @KarlKnechtel, but that is not my question. – JustBeingHelpful May 16 '22 at 17:32
  • Does this answer your question? [Importing installed package from script raises "AttributeError: module has no attribute" or "ImportError: cannot import name"](https://stackoverflow.com/questions/36250353/importing-installed-package-from-script-raises-attributeerror-module-has-no-at) – MisterMiyagi May 16 '22 at 17:36
  • 1
    Related [question](https://stackoverflow.com/questions/10501473/python-modules-with-identical-names-i-e-reusing-standard-module-names-in-pack). You'll get a lot of pushback here because this is just how it is, and you largely have to live with the outcome. That's a pragmatic approach, but hardly justifies the original design choice and its suboptimal consequences. It is one of a number of things that Python could have done better, imo. – jarmod May 16 '22 at 17:58

2 Answers2

1

This seems bizarre being how long Python has been around and is not fixed or the default install of Python doesn't avoid this, despite what modules are imported

It is not fixed or avoided because it is considered to be working as intended. In the default configuration, any time Python loads modules, it looks for the module in various paths, defined in a list that is accessible as sys.path.

From the documentation:

As initialized upon program startup, the first item of this list, path[0], is the directory containing the script that was used to invoke the Python interpreter. If the script directory is not available (e.g. if the interpreter is invoked interactively or if the script is read from standard input), path[0] is the empty string, which directs Python to search modules in the current directory first. Notice that the script directory is inserted before the entries inserted as a result of PYTHONPATH.

In your case, then, path[0] is C:\GitLab\p01, which is definitely not a place to look for standard library modules.

Personally, I am not very happy with the design decision (I would prefer, in particular, that new programmers are forced to get their heads around relative imports ASAP), but it is a conscious design decision, and one that enables quite a bit of convenience.

Is there a way to still keep the names the same but avoid the exception?

If you insist - you can simply hack sys.path:

import sys
local = sys.path.pop(0)
import requests
sys.path.insert(0, local)

This will not necessarily be compatible with any other hacks you might attempt to sys.path anywhere else in the program (but at least you can ensure that the change is immediately reverted after the otherwise-problematic imports). You will be stuck with something like this for every import (optimistically, sequence of imports) that conflicts with the name you want. And, of course, if you want to use the name sys you are out of luck.

You can wrap this up in a variety of ways; and you can hook into the import system in a variety of ways. It is possible (but potentially very dangerous) to do dynamic imports (i.e., from a string that contains a URI), and it is possible to alter the mechanisms that import uses. This is a broad research topic, beyond the scope of a Stack Overflow answer.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • I agree that the design is poor yet the way designed. I'll give you both a +1, but mark yours as correct. My question is more pathological anyway. – JustBeingHelpful May 16 '22 at 17:54
0

Python ships with a number of modules, such as json, collections, copy and email. A complete list can be found in the official documentation.

Since module/package names are globally unique per interpreter sessions, using custom modules/packages with the same name as a built in module makes the latter inaccessible by their given name. This means that any code trying to access the builtin modules – be it your own or third-party code – will likely fail.

There is no workaround for this. Do not re-use names of builtin modules/packages unless you purposely want to replace them. This includes removing leftover code with such naming conflicts. Either rename the conflicting files, or move them out of the searched paths.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
  • 1
    This is incomplete. It is important to understand Python's import mechanism for problems like this; in particular, *how and where it searches* for modules. Also, when "removing leftover code", you may also have to clean out cached `.pyc` files from a local `__pycache__` folder. – Karl Knechtel May 16 '22 at 17:32
  • @KarlKnechtel Do you know a good reference? I don't think going through all intricacies of the import system is helpful when the main message is "don't". – MisterMiyagi May 16 '22 at 17:34