Skip to content

Add type annotations to core classes and plugin infrastructure#3893

Open
arunpersaud wants to merge 7 commits into
getnikola:masterfrom
arunpersaud:types
Open

Add type annotations to core classes and plugin infrastructure#3893
arunpersaud wants to merge 7 commits into
getnikola:masterfrom
arunpersaud:types

Conversation

@arunpersaud

Copy link
Copy Markdown
Contributor

Pull Request Checklist

  • I’ve read the guidelines for contributing.
  • I updated AUTHORS.txt and CHANGES.txt (if the change is non-trivial) and documentation (if applicable).
  • I tested my changes.

Description

Adds type hints for site, logger, and various Nikola class attributes to improve type checking. Testing using pytest and 'ty'

@felixfontein felixfontein left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for working on this!

I guess this shows that adding type hints to an existing non-trivial Python codebase is hard. (I've been struggling with that myself more than once, even if you don't have the trouble of attributes that aren't always there.)

Regarding the attributes that aren't always there: maybe we should change them to attribute: type | None and initialize them with None?

Also regarding attribute type hints: I (nowadays) think it's better to add their types in __init__ than already in the class definition. The rationale is that when they are defined on the class level, the type checker won't notice if you use them too early in __init__ (before actually setting them). (They might still notice, I don't know how sophisticated they are nowadays, but better safe than sorry.) Also it's harder to forget to actually set an attribute that was declared to exist. Especially for complex constructors like Nikola.__init__ with its many hundreds of lines, this can make a difference.

Comment thread nikola/plugin_categories.py Outdated
Comment thread nikola/hierarchy_utils.py Outdated
Comment thread nikola/nikola.py Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since the type is above, it should be removed here:

Suggested change
self._template_system = None

Comment thread nikola/nikola.py
plugin_manager: PluginManager
_template_system: TemplateSystem
_template_system: TemplateSystem | None
tzinfo: datetime.tzinfo | None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How can tzinfo be None?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

tzinfo is set by dateutil.tz.gettz, which returns None on a non-valid timezone request

>>> dateutil.tz.gettz('asdfsadf') is None
True

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is using

>>> dateutil.__version__
'2.9.0.post0'

Perhaps in addition to the try-except block NIkola should check for None and do the same as in the try except?

Comment thread nikola/nikola.py Outdated
@arunpersaud

Copy link
Copy Markdown
Contributor Author

As for moving things to init. Happy to look into this. The main Nikola class now has

class Nikola:
    #[...]

    plugin_manager: PluginManager
    _template_system: TemplateSystem | None
    tzinfo: datetime.tzinfo | None
    themes_dirs: list[str]
    state: Persistor

And I think some, for example, state could just be deleted, since it is set to self.state = Persistor(...) and can be inferred by tools like ty? The other ones I can move where they are defined as None.

I could add this here or perhaps as another PR in the future for more work on the typing. Let me know what you prefer

@gaoflow

gaoflow commented Jun 19, 2026

Copy link
Copy Markdown

The current lint failure is from one unused import line in nikola/nikola.py:

nikola/nikola.py:30:1: F401 `datetime.timezone` imported but unused
nikola/nikola.py:30:1: F401 `datetime.tzinfo` imported but unused

The class annotation in this PR uses datetime.tzinfo | None, so the added direct import is not needed. Removing this line should fix the lint job:

 import datetime
-from datetime import timezone, tzinfo
 import io

The rest of the CI matrix is green.

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.

3 participants