Skip to content

gh-150459: Fix SyntaxError message for from x lazy import y#150877

Open
sobolevn wants to merge 4 commits into
python:mainfrom
sobolevn:issue-150459
Open

gh-150459: Fix SyntaxError message for from x lazy import y#150877
sobolevn wants to merge 4 commits into
python:mainfrom
sobolevn:issue-150459

Conversation

@sobolevn
Copy link
Copy Markdown
Member

@sobolevn sobolevn commented Jun 3, 2026

There's a problem with another branch of from ... import.
Because from .lazy import and from . lazy import is the same thing from the parser's point of view.

Things I've tried:

    | 'from' ('.' | '...')+ a="lazy" 'import' import_from_targets {
        RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a,
            "use 'lazy from ... ' instead of 'from ... lazy import'") }

What do I suggest? Maybe we can add an exception note for from .lazy import y when ImportError is raised with a similar message?


Update: Thanks a lot to @gpshead for the SyntaxWarning idea and draft implementation. See #150877 (comment)

Copy link
Copy Markdown
Member

@johnslavik johnslavik left a comment

Choose a reason for hiding this comment

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

❤️❤️❤️

Excellent

@gpshead
Copy link
Copy Markdown
Member

gpshead commented Jun 4, 2026

I think we can go further and detect this insignificant whitespace between the . and NAME tokens specifically when the name in question is lazy.

@sobolevn
Copy link
Copy Markdown
Member Author

sobolevn commented Jun 4, 2026

@gpshead I am not sure that this is really possible without introducing breaking changes.
For example, people might have this code in Python 3.14:

# lazy.py
x = 1

and

# example.py
from . lazy import x  # not-pep8 compatible, but working code
print(x)

If we change anything - we will break this code :(
What we can do is:

>>> from . lazy import x
<python-input-0>:2: ImportWarning: can't resolve package from __spec__ or __package__, falling back on __name__ and __path__
Traceback (most recent call last):
  File "<python-input-0>", line 2, in <module>
    from . lazy import x
ImportError: attempted relative import with no known parent package
Did you mean to use "lazy from . import"?

@gpshead
Copy link
Copy Markdown
Member

gpshead commented Jun 4, 2026

$ build/python -W always pkg/mod.py        # pkg/lazy.py exists, x = 42
pkg/mod.py:1: SyntaxWarning: 'from . lazy import' is the same as 'from .lazy import'; did you mean 'lazy from . import'?

is doable matching basically how the decimal literal special case works (thanks claude!). The bulk of the code winds up as in Parser/action_helpers.c and the unittest. everything else parser/grammar wise is pretty much one line changes if anyone wants to pick that up and run with it main...gpshead:cpython:claude/gifted-noether-pFmpK (i won't have time)

@pablogsal
Copy link
Copy Markdown
Member

This is may be a bit hacky though. In the world of the parser there are no more strings: just tokens. Although it's possible to reconstruct the "spacing" via the position information of the tokens this is a bit inelegant (at least with our parser theorist googles on) and I think is too much code for the small edge case that's this.

@sobolevn
Copy link
Copy Markdown
Member Author

sobolevn commented Jun 5, 2026

@gpshead @pablogsal I've implemented a much simplier warning code for this feature. Thanks a lot to @gpshead for the draft.

This is now ready to be reviewed. I would really like to get this into 3.15 as well, so it would be in sync with PEP-810 release. @hugovk can we do that, please? :)

@sobolevn
Copy link
Copy Markdown
Member Author

sobolevn commented Jun 5, 2026

Question: this seem unexpected that this warning happens twice in this case

» ./python.exe -c 'from . lazy import x'
<string>:1: SyntaxWarning: 'from . lazy import' is the same as 'from .lazy import'; did you mean 'lazy from . import'?
<string>:1: ImportWarning: can't resolve package from __spec__ or __package__, falling back on __name__ and __path__
Traceback (most recent call last):
<unknown>:1: SyntaxWarning: 'from . lazy import' is the same as 'from .lazy import'; did you mean 'lazy from . import'?
  File "<string>", line 1, in <module>
    from . lazy import x
ImportError: attempted relative import with no known parent package

What can be the cause of <unknown>:1: SyntaxWarning: 'from . lazy import' is the same as 'from .lazy import'; did you mean 'lazy from . import'?

@vstinner
Copy link
Copy Markdown
Member

vstinner commented Jun 5, 2026

test_peg_generator fails on the CI "Tests / Ubuntu / build and test (ubuntu-24.04-arm)":

ImportError: (...)/parse.cpython-316d-aarch64-linux-gnu.so: undefined symbol: _PyErr_EmitSyntaxWarning

You should export the symbol for test_peg_generator: use PyAPI_FUNC() in Include/internal/pycore_pyerrors.h and add a comment explaining that the symbol is needed by test_peg_generator.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants