Skip to content

Commit

Permalink
Merge pull request #2445 from samuelhwilliams/fix-typing
Browse files Browse the repository at this point in the history
Re-enable the `typing` github action
  • Loading branch information
samuelhwilliams authored Jul 15, 2024
2 parents afb95d7 + 29090c9 commit 0b5c387
Show file tree
Hide file tree
Showing 26 changed files with 736 additions and 128 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,12 @@ jobs:
strategy:
fail-fast: false
matrix:
# tox: ['docs', 'typing']
tox: ['docs'] # disable typing tests until we have typed the project correctly
tox: ['docs', 'typing']
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1
with:
python-version: '3.x'
python-version: 3.11
cache: pip
cache-dependency-path: requirements*/*.txt
- name: cache mypy
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ If you\'re using Homebrew on MacOS, you might need this:
# install postgis and geos
> brew install postgis
> brew install geos
> export DYLD_LIBRARY_PATH=/opt/homebrew/opt/geos/lib/

# set up postgresql user
> createuser -s postgresql
Expand Down
2 changes: 1 addition & 1 deletion flask_admin/_backwards.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import warnings

try:
from wtforms.widgets import HTMLString as Markup
from wtforms.widgets import HTMLString as Markup # type: ignore[attr-defined]
except ImportError:
# WTForms 2.3.0
from markupsafe import Markup # noqa: F401
Expand Down
39 changes: 14 additions & 25 deletions flask_admin/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,23 @@
:copyright: (c) 2013 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from typing import Callable

text_type = str
string_types = (str,)

itervalues = lambda d: iter(d.values())
iteritems = lambda d: iter(d.items())
filter_list = lambda f, l: list(filter(f, l))

def itervalues(d: dict):
return iter(d.values())


def iteritems(d: dict):
return iter(d.items())


def filter_list(f: Callable, l: list):
return list(filter(f, l))


def as_unicode(s):
if isinstance(s, bytes):
Expand All @@ -29,29 +39,8 @@ def csv_encode(s):
return as_unicode(s)


def with_metaclass(meta, *bases):
# This requires a bit of explanation: the basic idea is to make a
# dummy metaclass for one level of class instantiation that replaces
# itself with the actual metaclass. Because of internal type checks
# we also need to make sure that we downgrade the custom metaclass
# for one level to something closer to type (that's why __call__ and
# __init__ comes back from type etc.).
#
# This has the advantage over six.with_metaclass in that it does not
# introduce dummy classes into the final MRO.
class metaclass(meta):
__call__ = type.__call__
__init__ = type.__init__

def __new__(cls, name, this_bases, d):
if this_bases is None:
return type.__new__(cls, name, (), d)
return meta(name, bases, d)
return metaclass('temporary_class', None, {})


try:
# jinja2 3.0.0
from jinja2 import pass_context
from jinja2 import pass_context # type: ignore[attr-defined]
except ImportError:
from jinja2 import contextfunction as pass_context
6 changes: 6 additions & 0 deletions flask_admin/_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from typing import Union, Sequence, Dict, Callable

import sqlalchemy

T_COLUMN_LIST = Sequence[Union[str, sqlalchemy.Column]]
T_FORMATTERS = Dict[type, Callable] # todo: Make this tighter
2 changes: 1 addition & 1 deletion flask_admin/babel.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def get_translations_path(self, ctx):

wtforms_domain = Domain(messages_path(), domain='wtforms')

class Translations(object):
class Translations(object): # type: ignore[no-redef]
''' Fixes WTForms translation support and uses wtforms translations '''
def gettext(self, string):
t = wtforms_domain.get_translations()
Expand Down
4 changes: 2 additions & 2 deletions flask_admin/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from flask import Blueprint, current_app, render_template, abort, g, url_for
from flask_admin import babel
from flask_admin._compat import with_metaclass, as_unicode
from flask_admin._compat import as_unicode
from flask_admin import helpers as h

# For compatibility reasons import MenuLink
Expand Down Expand Up @@ -106,7 +106,7 @@ class BaseViewClass(object):
pass


class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
class BaseView(BaseViewClass, metaclass=AdminViewMeta):
"""
Base administrative view.
Expand Down
2 changes: 1 addition & 1 deletion flask_admin/contrib/fileadmin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ class MyAdmin(FileAdmin):
allowed_extensions = ('swf', 'jpg', 'gif', 'png')
"""

editable_extensions = tuple()
editable_extensions: tuple = tuple()
"""
List of editable extensions, in lower case.
Expand Down
4 changes: 4 additions & 0 deletions flask_admin/contrib/fileadmin/s3.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import time
from types import ModuleType
from typing import Optional

s3: Optional[ModuleType]

try:
from boto import s3
Expand Down
2 changes: 1 addition & 1 deletion flask_admin/contrib/geoa/typefmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ def geom_formatter(view, value):


DEFAULT_FORMATTERS = BASE_FORMATTERS.copy()
DEFAULT_FORMATTERS[WKBElement] = geom_formatter
DEFAULT_FORMATTERS[WKBElement] = geom_formatter # type: ignore[assignment]
2 changes: 1 addition & 1 deletion flask_admin/contrib/mongoengine/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,4 @@ class MongoImageField(MongoFileField):
GridFS image field.
"""

widget = widgets.MongoImageInput()
widget = widgets.MongoImageInput() # type: ignore[assignment]
2 changes: 1 addition & 1 deletion flask_admin/contrib/peewee/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
PrimaryKeyField, ForeignKeyField)

try:
from peewee import BaseModel
from peewee import BaseModel # type: ignore[attr-defined]
except ImportError:
from peewee import ModelBase as BaseModel

Expand Down
2 changes: 1 addition & 1 deletion flask_admin/contrib/sqla/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ class MyView(ModelView):
'languages': CheckboxListField,
}
"""
widget = CheckboxListInput()
widget = CheckboxListInput() # type: ignore[assignment]


class HstoreForm(BaseForm):
Expand Down
2 changes: 1 addition & 1 deletion flask_admin/contrib/sqla/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ def contribute(self, model, form_class, inline_model):


class InlineOneToOneModelConverter(InlineModelConverter):
inline_field_list_type = InlineModelOneToOneField
inline_field_list_type = InlineModelOneToOneField # type: ignore[assignment]

def _calculate_mapping_key_pair(self, model, info):

Expand Down
6 changes: 3 additions & 3 deletions flask_admin/contrib/sqla/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
from sqlalchemy.orm.clsregistry import _class_resolver
except ImportError:
# If 1.4/2.0 module import fails, fall back to <1.3.x architecture.
from sqlalchemy.ext.declarative.clsregistry import _class_resolver
from sqlalchemy.ext.declarative.clsregistry import _class_resolver # type: ignore[no-redef]
from sqlalchemy.ext.hybrid import hybrid_property
try:
# Attempt ASSOCATION_PROXY import from pre-2.0 release
from sqlalchemy.ext.associationproxy import ASSOCIATION_PROXY
except ImportError:
from sqlalchemy.ext.associationproxy import AssociationProxyExtensionType
from sqlalchemy.ext.associationproxy import AssociationProxyExtensionType # type: ignore[attr-defined]
ASSOCIATION_PROXY = AssociationProxyExtensionType.ASSOCIATION_PROXY
from sqlalchemy.sql.operators import eq
from sqlalchemy.sql.operators import eq # type: ignore[attr-defined]
from sqlalchemy.exc import DBAPIError
from sqlalchemy.orm.attributes import InstrumentedAttribute

Expand Down
6 changes: 3 additions & 3 deletions flask_admin/contrib/sqla/typefmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ def arrow_export_formatter(view, arrow_time):
})
try:
from sqlalchemy_utils import Choice
DEFAULT_FORMATTERS[Choice] = choice_formatter
DEFAULT_FORMATTERS[Choice] = choice_formatter # type: ignore[assignment]
except ImportError:
pass

try:
from arrow import Arrow
DEFAULT_FORMATTERS[Arrow] = arrow_formatter
EXPORT_FORMATTERS[Arrow] = arrow_export_formatter
DEFAULT_FORMATTERS[Arrow] = arrow_formatter # type: ignore[assignment]
EXPORT_FORMATTERS[Arrow] = arrow_export_formatter # type: ignore[assignment]
except ImportError:
pass
20 changes: 12 additions & 8 deletions flask_admin/contrib/sqla/view.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import warnings
import inspect
from typing import Optional, Dict, List, Tuple, cast as t_cast

from sqlalchemy.orm.attributes import InstrumentedAttribute
from sqlalchemy.orm.base import manager_of_class, instance_state
Expand Down Expand Up @@ -72,16 +73,19 @@ class PostAdmin(ModelView):
Please refer to the `subqueryload` on list of possible values.
"""

column_display_all_relations = ObsoleteAttr('column_display_all_relations',
'list_display_all_relations',
False)
column_display_all_relations = ObsoleteAttr(
'column_display_all_relations',
'list_display_all_relations',
False
)
"""
Controls if list view should display all relations, not only many-to-one.
"""

column_searchable_list = ObsoleteAttr('column_searchable_list',
'searchable_columns',
None)
column_searchable_list = t_cast(
None,
ObsoleteAttr('column_searchable_list', 'searchable_columns', None),
)
"""
Collection of the searchable columns.
Expand Down Expand Up @@ -264,9 +268,9 @@ class MyModelView(ModelView):
inline_models = (MyInlineModelForm(MyInlineModel),)
"""

column_type_formatters = DEFAULT_FORMATTERS
column_type_formatters = DEFAULT_FORMATTERS # type: ignore[assignment]

form_choices = None
form_choices: Optional[Dict[str, List[Tuple[str, str]]]] = None
"""
Map choices to form fields
Expand Down
2 changes: 1 addition & 1 deletion flask_admin/contrib/sqla/widgets.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from wtforms.widgets.core import escape
from wtforms.widgets.core import escape # type: ignore[attr-defined]

from flask_admin._backwards import Markup

Expand Down
9 changes: 7 additions & 2 deletions flask_admin/form/upload.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import os
import os.path as op
from types import ModuleType
from typing import Optional
from urllib.parse import urljoin

from werkzeug.utils import secure_filename
from werkzeug.datastructures import FileStorage

from wtforms import ValidationError, fields, __version__ as wtforms_version
from wtforms import ValidationError, fields, __version__ as wtforms_version # type: ignore[attr-defined]
from wtforms.utils import unset_value
from wtforms.widgets import html_params

Expand All @@ -15,6 +17,9 @@
from flask_admin._backwards import Markup
from flask_admin._compat import string_types

Image: Optional[ModuleType]
ImageOps: Optional[ModuleType]


try:
from PIL import Image, ImageOps
Expand Down Expand Up @@ -299,7 +304,7 @@ class ImageUploadField(FileUploadField):
Requires PIL (or Pillow) to be installed.
"""
widget = ImageUploadInput()
widget = ImageUploadInput() # type: ignore[assignment]

keep_image_formats = ('PNG',)
"""
Expand Down
Loading

0 comments on commit 0b5c387

Please sign in to comment.