Skip to content
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: 1 addition & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,11 @@ jobs:
# In order to make `exclude` option work, we need to separate the checks
# of returns and its tests into two separated commands
poetry run mypy --enable-error-code=unused-awaitable returns
poetry run mypy tests
poetry run mypy docs tests

# Different python versions are covered differently:
poetry run pytest returns docs/pages tests

poetry run codespell returns tests docs typesafety README.md CONTRIBUTING.md CHANGELOG.md

# TODO: re-enable after all problems are fixed
# poetry run poetry check
poetry run pip check
Expand Down
25 changes: 18 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,38 +1,49 @@
repos:
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.37.4
rev: 1a4bb160cab6417b3045e1b37b6b72449243e658 # frozen: 0.37.4
hooks:
- id: check-dependabot
- id: check-readthedocs
- id: check-github-workflows
- repo: https://github.com/rhysd/actionlint
rev: v1.7.12
rev: 914e7df21a07ef503a81201c76d2b11c789d3fca # frozen: v1.7.12
hooks:
- id: actionlint
- repo: https://github.com/woodruffw/zizmor-pre-commit
rev: v1.26.1
rev: 451b56af716f9f0d0c2b816503a3fd0cf8b036fa # frozen: v1.29.0
hooks:
- id: zizmor
args: ["--no-progress", "--fix"]
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.11.0.1
rev: 745eface02aef23e168a8afb6b5737818efbea95 # frozen: v0.11.0.1
hooks:
- id: shellcheck
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.21
rev: 39d9ac5938dadb73df0564a45f163e25ff9fa6e2 # frozen: v0.16.1
hooks:
- id: ruff
args: ["--exit-non-zero-on-fix"]
- id: ruff-format
- repo: https://github.com/sphinx-contrib/sphinx-lint
rev: v1.0.2
rev: c883505f64b59c3c5c9375191e4ad9f98e727ccd # frozen: v1.0.2
hooks:
- id: sphinx-lint
args: [--enable=default-role]
files: ^docs/

- repo: https://github.com/crate-ci/typos
rev: 96d9af6217dc2855210655af7e1ff19d23d4e593 # frozen: v1
hooks:
- id: typos
- repo: https://github.com/codespell-project/codespell
rev: 57b21406f092110c18776e39b0bda50d37c945c8 # frozen: v2.4.3
hooks:
- id: codespell
additional_dependencies:
- tomli

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
Expand Down
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ incremental in minor, bugfixes only are patches.
See [0Ver](https://0ver.org/).


## 0.28.1 WIP
## 0.29.0

### Features

- Add `mypy>=1.19,<2.4` support

### Bugfixes

Expand Down
17 changes: 4 additions & 13 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@

import pathlib
import sys

import tomli
import tomllib
from typing import Any

sys.path.insert(0, str(pathlib.Path('..').resolve()))


# -- Project information -----------------------------------------------------


def _get_project_meta():
def _get_project_meta() -> dict[str, Any]:
with pathlib.Path('../pyproject.toml').open(mode='rb') as pyproject:
return tomli.load(pyproject)['tool']['poetry']
return tomllib.load(pyproject)['tool']['poetry'] # type: ignore[no-any-return]


pkg_meta = _get_project_meta()
Expand Down Expand Up @@ -112,20 +112,11 @@ def _get_project_meta():
# a list of builtin themes.
html_theme = 'furo'

# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
html_sidebars = {}


# -- Extension configuration -------------------------------------------------

Expand Down
1,462 changes: 671 additions & 791 deletions poetry.lock

Large diffs are not rendered by default.

56 changes: 38 additions & 18 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ requires = ["poetry-core>=2"]

[tool.poetry]
name = "returns"
version = "0.28.0"
version = "0.29.0"
description = "Make your functions return something meaningful, typed, and safe!"
license = "BSD-3-Clause"

Expand Down Expand Up @@ -53,25 +53,24 @@ python = "^3.11"
typing-extensions = ">=4.0,<5.0"
pytest = {version = ">=8,<10", optional = true}
hypothesis = {version = "^6.151", optional = true}
mypy = {version = ">=1.19,<2.3", optional = true}
mypy = {version = ">=1.19,<2.4", optional = true}

[tool.poetry.extras]
compatible-mypy = ["mypy"]
check-laws = ["pytest", "hypothesis"]

[tool.poetry.group.dev.dependencies]
anyio = "^4.3"
trio = ">=0.30,<0.34"
attrs = ">=25.3,<27.0"
trio = "^0.33"
attrs = "^26.1"
httpx = "^0.28"
wemake-python-styleguide = "^1.6"
codespell = "^2.2"
slotscheck = ">=0.19,<0.21"
ruff = "^0.15"
pytest-cov = ">=6,<8"
pytest-randomly = ">=3.12,<5.0"
pytest-mypy-plugins = ">=3.1,<5.0"
pytest-subtests = ">=0.14,<0.16"
wemake-python-styleguide = "^1.7"
slotscheck = "^0.20"
ruff = "^0.16"
pytest-cov = "^7.1"
pytest-randomly = "^4.1"
pytest-mypy-plugins = "^4.0"
pytest-subtests = "^0.15"
pytest-shard = "^0.1"
pytest-codspeed = "^5.0"
covdefaults = "^2.3"
Expand All @@ -80,18 +79,18 @@ covdefaults = "^2.3"
optional = true

[tool.poetry.group.docs.dependencies]
sphinx = ">=8.1,<10.0"
sphinx-autodoc-typehints = ">=2.3,<4.0"
sphinxcontrib-mermaid = ">=1,<3"
furo = ">=2024.5,<2026.0"
myst-parser = ">=4,<6"
tomli = "^2.0"
sphinx = "^9.0"
sphinx-autodoc-typehints = "^3.6"
sphinxcontrib-mermaid = "^2.1"
furo = "^2025.12"
myst-parser = "^5.1"


[tool.ruff]
# Ruff config: https://docs.astral.sh/ruff/settings
preview = true
fix = true
output-prefer-rule-codes = true
target-version = "py311"
line-length = 80

Expand Down Expand Up @@ -159,6 +158,9 @@ ignore = [
"PLR09", # we have our own complexity rules
"PLR2004", # do not report magic numbers
"PLR6301", # do not require classmethod / staticmethod when self not used
"RUF105", # keep old `# noqa` comments style, not `# ruff` comments
"RUF106", # we prefer old error codes
"RUF201", # keep old rule codes instead of verbose names
"TRY003", # long exception messages from `tryceratops`
]
external = ["WPS"]
Expand Down Expand Up @@ -202,9 +204,27 @@ pydocstyle.convention = "google"
]
"tests/test_pattern_matching.py" = ["S101"]


[tool.slotscheck]
strict-imports = true
require-subclass = true
require-superclass = true
exclude-modules = "returns\\.contrib\\."
exclude-classes = "returns\\.primitives\\.exceptions:UnwrapFailedError"


[tool.codespell]
# codespell configuration: https://pypi.org/project/codespell
ignore-words-list = ["appliable", "falsy"]
skip = ["__pycache__", "_build", ".mypy_cache"]


[tool.typos]
# typos configuration: https://github.com/crate-ci/typos/
default.extend-ignore-identifiers-re = ["bimap"]
files.extend-exclude = [
"__pycache__",
"_build",
".mypy_cache",
"*.lock",
]
8 changes: 4 additions & 4 deletions returns/contrib/hypothesis/laws.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,22 +204,22 @@ def clean_plugin_context() -> Iterator[None]:

Otherwise, some types might be messed up.
"""
saved_stategies = {}
saved_strategies = {}
for strategy_key, strategy in types._global_type_lookup.items(): # noqa: SLF001
if isinstance( # type: ignore[redundant-expr, unused-ignore]
strategy_key,
type,
) and strategy_key.__module__.startswith('returns.'):
saved_stategies.update({strategy_key: strategy})
saved_strategies.update({strategy_key: strategy})

for key_to_remove in saved_stategies:
for key_to_remove in saved_strategies:
types._global_type_lookup.pop(key_to_remove) # noqa: SLF001
_clean_caches()

try:
yield
finally:
for saved_state in saved_stategies.items():
for saved_state in saved_strategies.items():
st.register_type_strategy(*saved_state)


Expand Down
6 changes: 3 additions & 3 deletions returns/future.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,17 +746,17 @@ def apply(
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure

>>> def appliable(x: int) -> int:
>>> def applicable(x: int) -> int:
... return x + 1

>>> assert anyio.run(
... FutureResult.from_value(1).apply(
... FutureResult.from_value(appliable),
... FutureResult.from_value(applicable),
... ).awaitable,
... ) == IOSuccess(2)
>>> assert anyio.run(
... FutureResult.from_failure(1).apply(
... FutureResult.from_value(appliable),
... FutureResult.from_value(applicable),
... ).awaitable,
... ) == IOFailure(1)

Expand Down
2 changes: 1 addition & 1 deletion returns/interfaces/specific/reader_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def from_result_context(
@final
class _LawSpec(LawSpecDef):
"""
Concrete laws for ``ReaderResulBasedN``.
Concrete laws for ``ReaderResultBasedN``.

See: https://github.com/haskell/mtl/pull/61/files
"""
Expand Down
10 changes: 5 additions & 5 deletions returns/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ def apply(
>>> from returns.curry import curry

>>> @curry
... def appliable(first: str, second: str) -> str:
... def applicable(first: str, second: str) -> str:
... return first + second

>>> assert IO('b').apply(IO('a').apply(IO(appliable))) == IO('ab')
>>> assert IO('b').apply(IO('a').apply(IO(applicable))) == IO('ab')

"""
return self.map(dekind(container)._inner_value) # noqa: SLF001
Expand Down Expand Up @@ -416,14 +416,14 @@ def apply(

>>> from returns.io import IOSuccess, IOFailure

>>> def appliable(first: str) -> str:
>>> def applicable(first: str) -> str:
... return first + 'b'

>>> assert IOSuccess('a').apply(
... IOSuccess(appliable),
... IOSuccess(applicable),
... ) == IOSuccess('ab')
>>> assert IOFailure('a').apply(
... IOSuccess(appliable),
... IOSuccess(applicable),
... ) == IOFailure('a')

>>> assert IOSuccess('a').apply(IOFailure(1)) == IOFailure(1)
Expand Down
6 changes: 3 additions & 3 deletions returns/maybe.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ def apply(

>>> from returns.maybe import Some, Nothing

>>> def appliable(string: str) -> str:
>>> def applicable(string: str) -> str:
... return string + 'b'

>>> assert Some('a').apply(Some(appliable)) == Some('ab')
>>> assert Some('a').apply(Some(applicable)) == Some('ab')
>>> assert Some('a').apply(Nothing) == Nothing
>>> assert Nothing.apply(Some(appliable)) == Nothing
>>> assert Nothing.apply(Some(applicable)) == Nothing
>>> assert Nothing.apply(Nothing) == Nothing

"""
Expand Down
4 changes: 2 additions & 2 deletions returns/pointfree/bind_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def bind_context2(

In other words, it modifies the function's
signature from:
``a -> RequresContext[b, c]``
``a -> RequiresContext[b, c]``
to:
``Container[a, c] -> Container[b, c]``

Expand Down Expand Up @@ -79,7 +79,7 @@ def bind_context3(
Composes successful container with a function that returns a container.

In other words, it modifies the function's
signature from: ``a -> RequresContext[b, c]``
signature from: ``a -> RequiresContext[b, c]``
to: ``Container[a, c] -> Container[b, c]``

.. code:: python
Expand Down
6 changes: 3 additions & 3 deletions returns/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,11 @@ def apply(

>>> from returns.result import Failure, Success

>>> def appliable(string: str) -> str:
>>> def applicable(string: str) -> str:
... return string + 'b'

>>> assert Success('a').apply(Success(appliable)) == Success('ab')
>>> assert Failure('a').apply(Success(appliable)) == Failure('a')
>>> assert Success('a').apply(Success(applicable)) == Success('ab')
>>> assert Failure('a').apply(Success(applicable)) == Failure('a')

>>> assert Success('a').apply(Failure(1)) == Failure(1)
>>> assert Failure(1).apply(Failure(2)) == Failure(1)
Expand Down
Loading