Skip to content

Add warning for implicit relative imports issues#49

Open
Eddy114514 wants to merge 3 commits intosoftdevteam:migrationfrom
Eddy114514:import_improve
Open

Add warning for implicit relative imports issues#49
Eddy114514 wants to merge 3 commits intosoftdevteam:migrationfrom
Eddy114514:import_improve

Conversation

@Eddy114514
Copy link
Copy Markdown
Collaborator

Python 2 supports implicit relative imports inside packages. A bare import inside a package can resolve to a sibling module.

Example package:

pkg/
  __init__.py
  foo.py
  main.py
# pkg/main.py
import foo

In Python 2, this can resolve to:

pkg.foo

In Python 3, imports are absolute by default, so the same code resolves as:

foo

This means the code can import a different top-level module or fail with ImportError.

For same environment of

# pkg/main.py
import foo

Python 2 resolves to pkg.foo
Python 3 resolves to top-level foo

I modified import.c to fire warning only if

  1. The import uses Python 2 implicit-relative semantics.
  2. The importing module has package context.
  3. The import is bare, not explicit-relative.
  4. Runtime resolution actually selected a package sibling module.

To ensure above, I checks whether the resolved module name is:
current_package + "." + imported_name
For example:

pkg + "." + foo == pkg.foo
If that condition is true, the import depended on Python 2 implicit relative import behavior, so pygrate2 emits a warning.

@Eddy114514 Eddy114514 self-assigned this May 2, 2026
Comment thread Lib/test/test_py3kwarn.py
package=True):
with test_support.temp_dir() as tmp:
if top_source is not None:
self._write_file(os.path.join(tmp, imported_name + '.py'),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this just open(..., 'w').write(...) in disguise?

Comment thread Python/import.c
PyObject *parent, *head, *next, *tail;
int from_import = 0;
int warn_if_relative_sibling = 0;
char package_name[MAXPATHLEN + 1];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are package names allowed to be MAXPATHLEN + 1 normally in CPython?

Comment thread Python/import.c
if (parent == NULL)
goto error_exit;

if (level < 0 && parent != Py_None && strchr(name, '.') == NULL &&
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How could buflen exceed sizeof(package_name)?

Comment thread Python/import.c
from_import = 1;
}

if (warn_if_relative_sibling) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&&

Comment thread Python/import.c
fix = PyString_FromFormat(
"use 'from .%.200s import ...' if the package sibling is intended",
imported_name);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} else on one line

Comment thread Python/import.c
imported_name);
}
if (msg == NULL || fix == NULL) {
Py_XDECREF(msg);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why decref NULL?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants