8000 gh-118465: Add __firstlineno__ attribute to class by serhiy-storchaka · Pull Request #118475 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-118465: Add __firstlineno__ attribute to class #118475

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 6, 2024

Conversation

serhiy-storchaka
Copy link
Member
@serhiy-storchaka serhiy-storchaka commented May 1, 2024

It is set by compiler with the line number of the first line of the class definition.


📚 Documentation preview 📚: https://cpython-previews--118475.org.readthedocs.build/

It is set by compiler with the line number of the first line of
the class definition.
@@ -280,6 +280,11 @@ Other Language Changes
class scopes are not inlined into their parent scope. (Contributed by
Jelle Zijlstra in :gh:`109118` and :gh:`118160`.)

* Classes have a new :attr:`!__firstlineno__` attribute,
populated by the compiler, with the line number of the first line
of the class definition.
Copy link
Member

Choose a reason for hiding this comment

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

If the class has decorators then I think this is the first line of the first decorator. Need a test for this case, and probably to mention in the doc.

Copy link
Member Author

Choose a reason for hiding this comment

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

There are existing tests, and they are passed.

I'll add explicit mentioning of this fact, but AFAIK it was not specified for co_firstlineno etc.

Co-authored-by: Mark Shannon <mark@hotpy.org>
@@ -280,6 +280,11 @@ Other Language Changes
class scopes are not inlined into their parent scope. (Contributed by
Jelle Zijlstra in :gh:`109118` and :gh:`118160`.)

* Classes have a new :attr:`!__firstlineno__` attribute,
populated by the compiler, with the line number of the first line
of the class definition.
Copy link
Member Author

Choose a reason for hiding this comment

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

There are existing tests, and they are passed.

I'll add explicit mentioning of this fact, but AFAIK it was not specified for co_firstlineno etc.

@JelleZijlstra
Copy link
Member

It is possible to construct classes without __firstlineno__:

def f():
    __firstlineno__ = 1
    class X:
        nonlocal __firstlineno__
    return X
print(f().__firstlineno__)  # AttributeError

Or:

class Y:
    global __firstlineno__
print(Y.__firstlineno__)  # AttributeError

This is obviously an extreme edge case though. You could also do this by manipulating locals() in the class body or the metaclass.

Is it worth it to make inspect smarter in the presence of classes without __firstlineno__?

  File "/Users/jelle/py/cpython/insp.py", line 18, in <module>
    print(inspect.findsource(X))
          ~~~~~~~~~~~~~~~~~~^^^
  File "/Users/jelle/py/cpython/Lib/inspect.py", line 1070, in findsource
    return lines, object.__firstlineno__ - 1
                  ^^^^^^^^^^^^^^^^^^^^^^
AttributeError: type object 'X' has no attribute '__firstlineno__'

@serhiy-storchaka
Copy link
Member Author

Good point. I thought that it is the same as for __module__ and __qualname__, but it turned out that they are set implicitly in the type constructor even when not specified in the class dict. They are set even for type('A', (), {}).

We can try to set __firstlineno__ to the current line number in the top Python frame, but it is safer to not set it at all. Setting __firstlineno__ for classes created by the type() call rather than in the class statement does not help in the only use case for this attribute.

I think that for backward compatibility getsource() should convert an AttributeError into OSError which was raised before for classes created by the type() call.

@serhiy-storchaka serhiy-storchaka merged commit 153b3f7 into python:main May 6, 2024
38 checks passed
@serhiy-storchaka serhiy-storchaka deleted the class-firstlineno branch May 6, 2024 09:02
SonicField pushed a commit to SonicField/cpython that referenced this pull request May 8, 2024
)

It is set by compiler with the line number of the first line of
the class definition.
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.

4 participants
0