Skip to content
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

Use all available ruff rules except those explicitly ignored #655

Merged
merged 2 commits into from
Jan 19, 2025
Merged
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
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ ci:
autoupdate_schedule: monthly
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.6
rev: v0.9.2
hooks:
- id: ruff
- id: ruff-format
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.30.0
rev: 0.31.0
hooks:
- id: check-github-workflows
- id: check-readthedocs
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
master_doc = "index"

project = "marshmallow-sqlalchemy"
copyright = "Steven Loria and contributors"
copyright = "Steven Loria and contributors" # noqa: A001

version = release = importlib.metadata.version("marshmallow-sqlalchemy")

Expand Down
54 changes: 45 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ include = ["docs/", "tests/", "CHANGELOG.rst", "CONTRIBUTING.rst", "tox.ini"]
exclude = ["docs/_build/"]

[tool.ruff]
src = ["src"]
src = ["src", "tests"]
fix = true
show-fixes = true
output-format = "full"
Expand All @@ -57,14 +57,50 @@ output-format = "full"
docstring-code-format = true

[tool.ruff.lint]
ignore = ["E203", "E266", "E501", "E731"]
select = [
"B", # flake8-bugbear
"E", # pycodestyle error
"F", # pyflakes
"I", # isort
"UP", # pyupgrade
"W", # pycodestyle warning
# use all checks available in ruff except the ones explicitly ignored below
select = ["ALL"]
ignore = [
"A005", # "module {name} shadows a Python standard-library module"
"ANN", # let mypy handle annotation checks
"ARG", # unused arguments are common w/ interfaces
"C901", # don't enforce complexity level
"COM", # let formatter take care commas
"D", # don't require docstrings
"DTZ007", # ignore false positives due to https://github.com/astral-sh/ruff/issues/1306
"E501", # leave line-length enforcement to formatter
"EM", # allow string messages in exceptions
"FIX", # allow "FIX" comments in code
"INP001", # allow Python files outside of packages
"N804", # metaclass methods aren't properly handled by this rule
"N806", # allow uppercase variable names for variables that are classes
"PERF203", # allow try-except within loops
"PLR0912", # "Too many branches"
"PLR0913", # "Too many arguments"
"PLR2004", # "Magic value used in comparison"
"PTH", # don't require using pathlib instead of os
"RUF012", # allow mutable class variables
"SIM102", # Sometimes nested ifs are more readable than if...and...
"SIM105", # "Use `contextlib.suppress(...)` instead of `try`-`except`-`pass`"
"SIM108", # sometimes if-else is more readable than a ternary
"SLF001", # allow private member access
"TD", # allow TODO comments to be whatever we want
"TRY003", # allow long messages passed to exceptions
"TRY004", # allow ValueError for invalid argument types
]

[tool.ruff.lint.per-file-ignores]
"tests/*" = [
"ARG", # unused arguments are fine in tests
"C408", # allow dict() instead of dict literal
"DTZ", # allow naive datetimes
"FBT003", # allow boolean positional argument
"N802", # allow uppercasing in test names, e.g. test_convert_TSVECTOR
"N803", # fixture names might be uppercase
"PLR0915", # allow lots of statements
"PT007", # ignore false positives due to https://github.com/astral-sh/ruff/issues/14743
"PT011", # don't require match when using pytest.raises
"S", # allow asserts
"SIM117", # allow nested with statements because it's more readable sometimes
]

[tool.mypy]
Expand Down
12 changes: 6 additions & 6 deletions src/marshmallow_sqlalchemy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
)

__all__ = [
"SQLAlchemySchema",
"ModelConversionError",
"ModelConverter",
"SQLAlchemyAutoSchema",
"SQLAlchemySchemaOpts",
"SQLAlchemyAutoSchemaOpts",
"SQLAlchemySchema",
"SQLAlchemySchemaOpts",
"auto_field",
"ModelConverter",
"fields_for_model",
"property2field",
"column2field",
"ModelConversionError",
"field_for",
"fields_for_model",
"property2field",
]
17 changes: 7 additions & 10 deletions src/marshmallow_sqlalchemy/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import functools
import inspect
import uuid
from collections.abc import Iterable
from typing import (
TYPE_CHECKING,
Any,
Expand All @@ -25,14 +24,16 @@
from marshmallow import fields, validate
from sqlalchemy.dialects import mssql, mysql, postgresql
from sqlalchemy.orm import SynonymProperty
from sqlalchemy.types import TypeEngine

from .exceptions import ModelConversionError
from .fields import Related, RelatedList

if TYPE_CHECKING:
from collections.abc import Iterable

from sqlalchemy.ext.declarative import DeclarativeMeta
from sqlalchemy.orm import MapperProperty
from sqlalchemy.types import TypeEngine

PropertyOrColumn = MapperProperty | sa.Column

Expand Down Expand Up @@ -136,8 +137,7 @@ def __init__(self, schema_cls: type[ma.Schema] | None = None):
def type_mapping(self) -> dict[type, type[fields.Field]]:
if self.schema_cls:
return self.schema_cls.TYPE_MAPPING
else:
return ma.Schema.TYPE_MAPPING
return ma.Schema.TYPE_MAPPING

def fields_for_model(
self,
Expand Down Expand Up @@ -349,14 +349,13 @@ def field_for(
converted_prop = self.property2field(
prop,
# To satisfy type checking, need to pass a literal bool
instance=True if instance else False,
instance=True if instance else False, # noqa: SIM210
field_class=field_class,
**kwargs,
)
if remote_with_local_multiplicity:
return RelatedList(converted_prop, **{**self.get_base_kwargs(), **kwargs})
else:
return converted_prop
return converted_prop

def _get_field_name(self, prop_or_column: PropertyOrColumn) -> str:
return prop_or_column.key
Expand Down Expand Up @@ -478,9 +477,7 @@ def _should_exclude_field(
key = self._get_field_name(column)
if fields and key not in fields:
return True
if exclude and key in exclude:
return True
return False
return bool(exclude and key in exclude)

def get_base_kwargs(self):
return {"validate": [], "metadata": {}}
Expand Down
2 changes: 1 addition & 1 deletion src/marshmallow_sqlalchemy/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def transient(self):

def _serialize(self, value, attr, obj):
ret = {prop.key: getattr(value, prop.key, None) for prop in self.related_keys}
return ret if len(ret) > 1 else list(ret.values())[0]
return ret if len(ret) > 1 else next(iter(ret.values()))

def _deserialize(self, value, *args, **kwargs):
"""Deserialize a serialized value to a model instance.
Expand Down
6 changes: 4 additions & 2 deletions src/marshmallow_sqlalchemy/load_instance_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@

from __future__ import annotations

from collections.abc import Iterable, Mapping
from typing import Any, Generic, TypeVar, cast
from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast

import marshmallow as ma
from sqlalchemy.ext.declarative import DeclarativeMeta
Expand All @@ -18,6 +17,9 @@

from .fields import get_primary_keys

if TYPE_CHECKING:
from collections.abc import Iterable, Mapping

_ModelType = TypeVar("_ModelType", bound=DeclarativeMeta)


Expand Down
13 changes: 7 additions & 6 deletions src/marshmallow_sqlalchemy/schema.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
from __future__ import annotations

from typing import Any, cast
from typing import TYPE_CHECKING, Any, cast

import sqlalchemy as sa
from marshmallow.fields import Field
from marshmallow.schema import Schema, SchemaMeta, SchemaOpts
from sqlalchemy.ext.declarative import DeclarativeMeta

from .convert import ModelConverter
from .exceptions import IncorrectSchemaTypeError
from .load_instance_mixin import LoadInstanceMixin, _ModelType

if TYPE_CHECKING:
from sqlalchemy.ext.declarative import DeclarativeMeta


# This isn't really a field; it's a placeholder for the metaclass.
# This should be considered private API.
Expand Down Expand Up @@ -42,10 +44,9 @@ def create_field(
model = self.model or schema_opts.model
if model:
return converter.field_for(model, column_name, **self.field_kwargs)
else:
table = self.table if self.table is not None else schema_opts.table
column = getattr(cast(sa.Table, table).columns, column_name)
return converter.column2field(column, **self.field_kwargs)
table = self.table if self.table is not None else schema_opts.table
column = getattr(cast(sa.Table, table).columns, column_name)
return converter.column2field(column, **self.field_kwargs)

# This field should never be bound to a schema.
# If this method is called, it's probably because the schema is not a SQLAlchemySchema.
Expand Down
10 changes: 4 additions & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,26 @@
class AnotherInteger(sa.Integer):
"""Use me to test if MRO works like we want"""

pass


class AnotherText(sa.types.TypeDecorator):
"""Use me to test if MRO and `impl` virtual type works like we want"""

impl = sa.UnicodeText


@pytest.fixture()
@pytest.fixture
def Base() -> type:
return declarative_base()


@pytest.fixture()
@pytest.fixture
def engine():
engine = sa.create_engine("sqlite:///:memory:", echo=False, future=True)
yield engine
engine.dispose()


@pytest.fixture()
@pytest.fixture
def session(Base, models, engine):
Session = sessionmaker(bind=engine)
Base.metadata.create_all(bind=engine)
Expand All @@ -75,7 +73,7 @@ class Models:
Keyword: type[DeclarativeMeta]


@pytest.fixture()
@pytest.fixture
def models(Base: type) -> Models:
# models adapted from https://github.com/wtforms/wtforms-sqlalchemy/blob/master/tests/tests.py
student_course = sa.Table(
Expand Down
3 changes: 1 addition & 2 deletions tests/test_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def make_property(*column_args, **column_kwargs):


class TestPropertyFieldConversion:
@pytest.fixture()
@pytest.fixture
def converter(self):
return ModelConverter()

Expand All @@ -185,7 +185,6 @@ class MySchema(Schema):
(sa.Date, fields.Date),
(sa.DateTime, fields.DateTime),
(sa.Boolean, fields.Bool),
(sa.Boolean, fields.Bool),
(sa.Float, fields.Float),
(sa.SmallInteger, fields.Int),
(sa.Interval, fields.TimeDelta),
Expand Down
Loading