Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .meta.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,14 @@ per-file-ignores =

[pyproject]
codespell_ignores = "ist,"

[pre-commit]
extra_lines = """
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.18.2
hooks:
- id: mypy
additional_dependencies:
- types-decorator
exclude: docs
"""
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,10 @@ repos:
# _your own configuration lines_
# """
##
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.18.2
hooks:
- id: mypy
additional_dependencies:
- types-decorator
exclude: docs
1 change: 1 addition & 0 deletions news/+d8c22f55.internal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add type hints [@ale-rt]
2 changes: 2 additions & 0 deletions news/+meta.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Update configuration files.
[plone devs]
39 changes: 23 additions & 16 deletions src/plone/base/batch.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
from plone.batching.batch import QuantumBatch
from plone.batching.utils import calculate_pagerange
from typing import Any
from typing import Dict
from typing import Optional
from typing import Union
from zope.deprecation import deprecate
from ZTUtils import make_query
from ZTUtils.Lazy import LazyMap


class Batch(QuantumBatch):
b_start_str = "b_start"

def __init__(
self,
sequence,
size,
start=0,
end=0,
orphan=0,
overlap=0,
pagerange=7,
quantumleap=0,
b_start_str="b_start",
sequence: Union[range, LazyMap],
size: int,
start: int = 0,
end: int = 0,
orphan: int = 0,
overlap: int = 0,
pagerange: int = 7,
quantumleap: int = 0,
b_start_str: str = "b_start",
):
super().__init__(
sequence, size, start, end, orphan, overlap, pagerange, quantumleap
Expand Down Expand Up @@ -46,35 +51,37 @@ def __bool__(self):
# For Python 2 compatibility:
__nonzero__ = __bool__

def initialize(self, start, end, size):
def initialize(self, start: int, end: int, size: int):
super().initialize(start, end, size)
self.pagerange, self.pagerangestart, self.pagerangeend = calculate_pagerange(
self.pagenumber, self.numpages, self.pagerange
self.pagenumber, self.numpages, self.pagerange # type: ignore[has-type]
)

def pageurl(self, formvariables, pagenumber=-1):
def pageurl(self, formvariables: Dict[Any, Any], pagenumber: int = -1) -> str:
# Makes the url for a given page.
if pagenumber == -1:
pagenumber = self.pagenumber
b_start = pagenumber * (self.pagesize - self.overlap) - self.pagesize
return make_query(formvariables, {self.b_start_str: b_start})

def navurls(self, formvariables, navlist=None):
def navurls(
self, formvariables: Dict[Any, Any], navlist: Optional[range] = None
) -> map:
# Returns the page number and url for the navigation quick links.
if navlist is None:
navlist = []
navlist = range(0)
if not navlist:
navlist = self.navlist
return map(
lambda x, formvariables=formvariables: (x, self.pageurl(formvariables, x)),
navlist,
)

def prevurls(self, formvariables):
def prevurls(self, formvariables: Dict[Any, Any]) -> map:
# Helper method to get prev navigation list from templates.
return self.navurls(formvariables, self.previous_pages)

def nexturls(self, formvariables):
def nexturls(self, formvariables: Dict[Any, Any]) -> map:
# Helper method to get next navigation list from templates.
return self.navurls(formvariables, self.next_pages)

Expand Down
61 changes: 37 additions & 24 deletions src/plone/base/i18nl10n.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
"""

from Acquisition import aq_acquire
from datetime import datetime
from DateTime import DateTime
from DateTime.interfaces import IDateTime
from plone.base.tests.test_i18nl10n import DummyContext
from plone.registry.interfaces import IRegistry
from typing import cast
from typing import Optional
from typing import Union
from zope.component import getUtility
from zope.i18n import translate
from zope.i18n.locales import locales
from zope.publisher.browser import TestRequest
from zope.publisher.interfaces.browser import IBrowserRequest

import logging
Expand Down Expand Up @@ -128,8 +134,13 @@ def setDefaultTimeFormat(localeid, value):


def utranslate(
domain, msgid, mapping=None, context=None, target_language=None, default=None
):
domain: str,
msgid: str,
mapping: None = None,
context: Optional[TestRequest] = None,
target_language: Optional[str] = None,
default: Optional[str] = None,
) -> str:
# We used to pass an object as context.
if not IBrowserRequest.providedBy(context):
context = aq_acquire(context, "REQUEST")
Expand All @@ -156,14 +167,14 @@ def get_formatstring_from_registry(msgid):


def ulocalized_time(
time,
long_format=None,
time_only=False,
context=None,
domain="plonelocales",
request=None,
target_language=None,
):
time: Optional[Union[datetime, str, int]],
long_format: Optional[Union[str, bool]] = None,
time_only: Optional[bool] = False,
context: Optional[DummyContext] = None,
domain: Optional[str] = "plonelocales",
request: None = None,
target_language: Optional[str] = None,
) -> Optional[str]:
"""unicode aware localized time method (l10n)"""

if time_only:
Expand Down Expand Up @@ -208,12 +219,12 @@ def ulocalized_time(
try:
time = DateTime(time)
except Exception:
logger.debug(f"Failed to convert {time} to a DateTime object")
logger.debug("Failed to convert %s to a DateTime object", time)
return None

if context is None:
# when without context, we cannot do very much.
return time.ISO8601()
return cast(DateTime, time).ISO8601()

if request is None:
request = aq_acquire(context, "REQUEST")
Expand All @@ -233,7 +244,7 @@ def ulocalized_time(
# e.g. %Y instead of ${Y} for the year. This means we cannot do further
# i18n/l10n for translating week days or month names. Python will do
# translation using the current locale.
return time.strftime(formatstring)
return cast(DateTime, time).strftime(formatstring)
else:
# 2. the normal case: translation machinery,
# that is the ".../LC_MESSAGES/plonelocales.po" files
Expand All @@ -251,26 +262,26 @@ def ulocalized_time(
formatstring = "%H:%M" # 03:14
else:
formatstring = "[INTERNAL ERROR]"
return time.strftime(formatstring)
return cast(DateTime, time).strftime(formatstring)

# get the format elements used in the formatstring
formatelements = {el[2:-1] for el in _interp_regex.findall(formatstring)}

# add used elements to mapping
elements = formatelements & datetime_formatvariables
for key in elements:
mapping[key] = time.strftime("%" + key)
mapping[key] = cast(DateTime, time).strftime("%" + key)

# add weekday name, abbr. weekday name, month name, abbr month name
name_elements = formatelements & name_formatvariables
if {"a", "A"} & name_elements:
weekday = int(time.strftime("%w")) # weekday, sunday = 0
weekday = int(cast(DateTime, time).strftime("%w")) # weekday, sunday = 0
if "a" in name_elements:
mapping["a"] = weekdayname_msgid_abbr(weekday)
if "A" in name_elements:
mapping["A"] = weekdayname_msgid(weekday)
if {"b", "B"} & name_elements:
monthday = int(time.strftime("%m")) # month, january = 1
monthday = int(cast(DateTime, time).strftime("%m")) # month, january = 1
if "b" in name_elements:
mapping["b"] = monthname_msgid_abbr(monthday)
if "B" in name_elements:
Expand All @@ -292,7 +303,9 @@ def ulocalized_time(
)


def _numbertoenglishname(number, format=None, attr="_days"):
def _numbertoenglishname(
number: int, format: Optional[str] = None, attr: str = "_days"
) -> str:
# returns the english name of day or month number
# starting with Sunday == 0
# and January = 1
Expand All @@ -307,38 +320,38 @@ def _numbertoenglishname(number, format=None, attr="_days"):
return ENGLISH_NAMES[attr][number]


def monthname_english(number, format=None):
def monthname_english(number: int, format: Optional[str] = None) -> str:
# returns the english name of month with number
return _numbertoenglishname(number, format=format, attr="_months")


def weekdayname_english(number, format=None):
def weekdayname_english(number: int, format: Optional[str] = None) -> str:
# returns the english name of week with number
return _numbertoenglishname(number, format=format, attr="_days")


def monthname_msgid(number):
def monthname_msgid(number: int) -> str:
# returns the msgid for monthname
# use to translate to full monthname (January, February, ...)
# e.g. month_jan, month_feb, ...
return "month_%s" % monthname_english(number, format="a").lower()


def monthname_msgid_abbr(number):
def monthname_msgid_abbr(number: int) -> str:
# returns the msgid for the abbreviated monthname
# use to translate to abbreviated format (Jan, Feb, ...)
# e.g. month_jan_abbr, month_feb_abbr, ...
return "month_%s_abbr" % monthname_english(number, format="a").lower()


def weekdayname_msgid(number):
def weekdayname_msgid(number: int) -> str:
# returns the msgid for the weekdayname
# use to translate to full weekdayname (Monday, Tuesday, ...)
# e.g. weekday_mon, weekday_tue, ...
return "weekday_%s" % weekdayname_english(number, format="a").lower()


def weekdayname_msgid_abbr(number):
def weekdayname_msgid_abbr(number: int) -> str:
# returns the msgid for abbreviated weekdayname
# use to translate to abbreviated format (Mon, Tue, ...)
# e.g. weekday_mon_abbr, weekday_tue_abbr, ...
Expand Down
1 change: 1 addition & 0 deletions src/plone/base/interfaces/constrains.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# mypy: disable-error-code=misc
from zope.interface import Interface


Expand Down
13 changes: 7 additions & 6 deletions src/plone/base/interfaces/controlpanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from plone import schema
from plone.base import PloneMessageFactory as _
from Products.CMFCore.utils import getToolByName
from typing import Dict
Copy link
Member

Choose a reason for hiding this comment

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

Not necessary anymore.
Since Python 3.9 we an use built in types like tuple, list and dict as generic types for type hinting.

from zope.component.hooks import getSite
from zope.interface import Attribute
from zope.interface import implementer
Expand All @@ -15,7 +16,7 @@
import json


def dump_json_to_text(obj):
def dump_json_to_text(obj: Dict[str, Dict[str, str]]) -> str:
"""Encode an obj into a text"""
text = json.dumps(obj, indent=4)
if not isinstance(text, str):
Expand Down Expand Up @@ -59,7 +60,7 @@ def dump_json_to_text(obj):
"""


def validate_json(value):
def validate_json(value: str):
try:
json.loads(value)
except ValueError as exc:
Expand Down Expand Up @@ -100,10 +101,10 @@ def unregisterConfiglet(id):
def unregisterApplication(appId):
"""unregister Application with all configlets"""

def getGroupIds():
def getGroupIds(): # type: ignore[misc]
"""list of the group ids"""

def getGroups():
def getGroups(): # type: ignore[misc]
"""list of groups as dicts with id and title"""

def enumConfiglets(group=None):
Expand Down Expand Up @@ -1567,7 +1568,7 @@ class IUserGroupsSettingsSchema(Interface):
)


def validate_twitter_username(value):
def validate_twitter_username(value: str):
if value and value.startswith("@"):
raise Invalid('Twitter username should not include the "@" prefix character.')
return True
Expand Down Expand Up @@ -1959,7 +1960,7 @@ class IPloneControlPanelView(Interface):
class IPloneControlPanelForm(IPloneControlPanelView):
"""Forms using plone.app.controlpanel"""

def _on_save():
def _on_save(): # type: ignore[misc]
"""Callback method which can be implemented by control panels to
react when the form is successfully saved. This avoids the need
to re-define actions only to do some additional notification or
Expand Down
1 change: 1 addition & 0 deletions src/plone/base/interfaces/defaultpage.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# mypy: disable-error-code=misc
Copy link
Member

Choose a reason for hiding this comment

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

Do we really need this?
Also, mypy is just one of the type checkers - I'd avoid picking and configuring one.
Maybe there is a global config, so that we do not have to repeat this for every module, where some typing errors occur.

from zope.interface import Interface


Expand Down
1 change: 1 addition & 0 deletions src/plone/base/interfaces/images.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# mypy: disable-error-code=misc
from zope.interface import Interface


Expand Down
1 change: 1 addition & 0 deletions src/plone/base/interfaces/installable.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# mypy: disable-error-code=misc
from zope.interface import Interface


Expand Down
1 change: 1 addition & 0 deletions src/plone/base/interfaces/migration.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# mypy: disable-error-code=misc
from zope.interface import Interface


Expand Down
1 change: 1 addition & 0 deletions src/plone/base/interfaces/password_reset.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# mypy: disable-error-code=misc
from zope.interface import Attribute
from zope.interface import Interface

Expand Down
1 change: 1 addition & 0 deletions src/plone/base/interfaces/properties.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# mypy: disable-error-code=misc
from zope.interface import Attribute
from zope.interface import Interface

Expand Down
1 change: 1 addition & 0 deletions src/plone/base/interfaces/syndication.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# mypy: disable-error-code=misc
from .. import PloneMessageFactory as _
from zope import schema
from zope.interface import Interface
Expand Down
Loading