Skip to content

Commit 0811ef0

Browse files
added ruff linter and apply some lints
1 parent f9d62d7 commit 0811ef0

21 files changed

+883
-867
lines changed

.github/workflows/main.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ jobs:
3030
run: |
3131
task check-version
3232
33+
# TODO
34+
# - name: Run code linter
35+
# run: |
36+
# pip install ruff
37+
# task lint
38+
3339
test:
3440
needs: feature-branch-checks
3541
runs-on: ubuntu-latest

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
- removed `create_pdoc.sh`
1212
- moved `examples` out of the package
1313
- CI: added a check that `CHANGELOG.md` is modified on feature branches
14-
- CI: added a check that the poetry package version was updated
14+
- CI: added a check that the poetry package version was updated
15+
- added `ruff` linter and apply lint rules to code
16+
- updated `README.md`
1517

1618
## Pedantic 2.4.0
1719
- migrate from unittest to pytest

README.md

Lines changed: 1 addition & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,7 @@
1-
# pedantic-python-decorators [![Build Status](https://travis-ci.com/LostInDarkMath/pedantic-python-decorators.svg?branch=master)](https://travis-ci.com/LostInDarkMath/pedantic-python-decorators) [![Coverage Status](https://coveralls.io/repos/github/LostInDarkMath/pedantic-python-decorators/badge.svg?branch=master)](https://coveralls.io/github/LostInDarkMath/pedantic-python-decorators?branch=master) [![PyPI version](https://badge.fury.io/py/pedantic.svg)](https://badge.fury.io/py/pedantic) [![Conda Version](https://img.shields.io/conda/vn/conda-forge/pedantic.svg)](https://anaconda.org/conda-forge/pedantic) [![Last Commit](https://badgen.net/github/last-commit/LostInDarkMath/pedantic-python-decorators?color=green)](https://GitHub.com/LostInDarkMath/pedantic-python-decorators) [![Stars](https://badgen.net/github/stars/LostInDarkMath/pedantic-python-decorators?color=green)](https://GitHub.com/LostInDarkMath/pedantic-python-decorators) [![Open Issues](https://badgen.net/github/open-issues/LostInDarkMath/pedantic-python-decorators?color=green)](https://GitHub.com/LostInDarkMath/pedantic-python-decorators/issues) [![Open PRs](https://badgen.net/github/open-prs/LostInDarkMath/pedantic-python-decorators?color=green)](https://GitHub.com/LostInDarkMath/pedantic-python-decorators/pulls)
1+
# pedantic-python-decorators [![Coverage Status](https://coveralls.io/repos/github/LostInDarkMath/pedantic-python-decorators/badge.svg?branch=master)](https://coveralls.io/github/LostInDarkMath/pedantic-python-decorators?branch=master) [![PyPI version](https://badge.fury.io/py/pedantic.svg)](https://badge.fury.io/py/pedantic) [![Conda Version](https://img.shields.io/conda/vn/conda-forge/pedantic.svg)](https://anaconda.org/conda-forge/pedantic) [![Last Commit](https://badgen.net/github/last-commit/LostInDarkMath/pedantic-python-decorators?color=green)](https://GitHub.com/LostInDarkMath/pedantic-python-decorators) [![Stars](https://badgen.net/github/stars/LostInDarkMath/pedantic-python-decorators?color=green)](https://GitHub.com/LostInDarkMath/pedantic-python-decorators) [![Open Issues](https://badgen.net/github/open-issues/LostInDarkMath/pedantic-python-decorators?color=green)](https://GitHub.com/LostInDarkMath/pedantic-python-decorators/issues) [![Open PRs](https://badgen.net/github/open-prs/LostInDarkMath/pedantic-python-decorators?color=green)](https://GitHub.com/LostInDarkMath/pedantic-python-decorators/pulls)
22

33
This packages includes many decorators that will make you write cleaner Python code.
44

5-
## Getting Started
6-
This package requires Python 3.11 or later.
7-
There are multiple options for installing this package.
8-
9-
### Option 1: Installing with pip from [Pypi](https://pypi.org/)
10-
Run `pip install pedantic`.
11-
12-
### Option 2: Installing with conda from [conda-forge](conda-forge.org)
13-
Run `conda install -c conda-forge pedantic`
14-
15-
### Option 3: Installing with pip and git
16-
1. Install [Git](https://git-scm.com/downloads) if you don't have it already.
17-
2. Run `pip install git+https://github.com/LostInDarkMath/pedantic-python-decorators.git@master`
18-
19-
### Option 4: Offline installation using wheel
20-
1. Download the [latest release here](https://github.com/LostInDarkMath/PythonHelpers/releases/latest) by clicking on `pedantic-python-decorators-x.y.z-py-none-any.whl`.
21-
2. Execute `pip install pedantic-python-decorators-x.y.z-py3-none-any.whl`.
22-
235
## The [@pedantic](https://lostindarkmath.github.io/pedantic-python-decorators/pedantic/method_decorators.html#pedantic.method_decorators.pedantic) decorator - Type checking at runtime
246
The `@pedantic` decorator does the following things:
257
- The decorated function can only be called by using keyword arguments. Positional arguments are not accepted.
@@ -50,98 +32,6 @@ get_sum_of(values=[0, 1.2, 3, 5.4]) # this raises the following runtime error:
5032

5133
## The [@validate]() decorator
5234
As the name suggests, with `@validate` you are able to validate the values that are passed to the decorated function.
53-
That is done in a highly customizable way.
54-
But the highest benefit of this decorator is that it makes it extremely easy to write decoupled easy testable, maintainable and scalable code.
55-
The following example shows the decoupled implementation of a configurable algorithm with the help of `@validate`:
56-
```python
57-
import os
58-
from dataclasses import dataclass
59-
60-
from pedantic import validate, ExternalParameter, overrides, Validator, Parameter, Min, ReturnAs
61-
62-
63-
@dataclass(frozen=True)
64-
class Configuration:
65-
iterations: int
66-
max_error: float
67-
68-
69-
class ConfigurationValidator(Validator):
70-
@overrides(Validator)
71-
def validate(self, value: Configuration) -> Configuration:
72-
if value.iterations < 1 or value.max_error < 0:
73-
self.raise_exception(msg=f'Invalid configuration: {value}', value=value)
74-
75-
return value
76-
77-
78-
class ConfigFromEnvVar(ExternalParameter):
79-
""" Reads the configuration from environment variables. """
80-
81-
@overrides(ExternalParameter)
82-
def has_value(self) -> bool:
83-
return 'iterations' in os.environ and 'max_error' in os.environ
84-
85-
@overrides(ExternalParameter)
86-
def load_value(self) -> Configuration:
87-
return Configuration(
88-
iterations=int(os.environ['iterations']),
89-
max_error=float(os.environ['max_error']),
90-
)
91-
92-
93-
class ConfigFromFile(ExternalParameter):
94-
""" Reads the configuration from a config file. """
95-
96-
@overrides(ExternalParameter)
97-
def has_value(self) -> bool:
98-
return os.path.isfile('config.csv')
99-
100-
@overrides(ExternalParameter)
101-
def load_value(self) -> Configuration:
102-
with open(file='config.csv', mode='r') as file:
103-
content = file.readlines()
104-
return Configuration(
105-
iterations=int(content[0].strip('\n')),
106-
max_error=float(content[1]),
107-
)
108-
109-
110-
# choose your configuration source here:
111-
@validate(ConfigFromEnvVar(name='config', validators=[ConfigurationValidator()]), strict=False, return_as=ReturnAs.KWARGS_WITH_NONE)
112-
# @validate(ConfigFromFile(name='config', validators=[ConfigurationValidator()]), strict=False)
113-
114-
# with strict_mode = True (which is the default)
115-
# you need to pass a Parameter for each parameter of the decorated function
116-
# @validate(
117-
# Parameter(name='value', validators=[Min(5, include_boundary=False)]),
118-
# ConfigFromFile(name='config', validators=[ConfigurationValidator()]),
119-
# )
120-
def my_algorithm(value: float, config: Configuration) -> float:
121-
"""
122-
This method calculates something that depends on the given value with considering the configuration.
123-
Note how well this small piece of code is designed:
124-
- Fhe function my_algorithm() need a Configuration but has no knowledge where this come from.
125-
- Furthermore, it doesn't care about parameter validation.
126-
- The ConfigurationValidator doesn't know anything about the creation of the data.
127-
- The @validate decorator is the only you need to change, if you want a different configuration source.
128-
"""
129-
print(value)
130-
print(config)
131-
return value
132-
133-
134-
if __name__ == '__main__':
135-
# we can call the function with a config like there is no decorator.
136-
# This makes testing extremely easy: no config files, no environment variables or stuff like that
137-
print(my_algorithm(value=2, config=Configuration(iterations=3, max_error=4.4)))
138-
139-
os.environ['iterations'] = '12'
140-
os.environ['max_error'] = '3.1415'
141-
142-
# but we also can omit the config and load it implicitly by our custom Parameters
143-
print(my_algorithm(value=42.0))
144-
```
14535

14636
## List of all decorators in this package
14737
- [@deprecated](https://lostindarkmath.github.io/pedantic-python-decorators/pedantic/decorators/fn_deco_deprecated.html)
@@ -196,6 +86,4 @@ enable_pedantic()
19686
This package is **not** compatible with compiled source code (e.g. with [Nuitka](https://github.com/Nuitka/Nuitka)).
19787
That's because it uses the `inspect` module from the standard library which will raise errors like `OSError: could not get source code` in case of compiled source code.
19888

199-
20089
Don't forget to check out the [documentation](https://lostindarkmath.github.io/pedantic-python-decorators/pedantic).
201-
Happy coding!

Taskfile.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,25 @@ tasks:
2929
- pytest --doctest-modules --cov=pedantic --cov-branch --cov-report= --cov-report=term
3030
silent: true
3131

32+
lint:
33+
desc: Runs the linter (ruff)
34+
silent: true
35+
cmds:
36+
- ruff check pedantic
37+
38+
lint-fix:
39+
desc: Runs the linter and fixes all fixable errors automatically
40+
silent: true
41+
cmds:
42+
- ruff check pedantic --fix
43+
3244
validate:
3345
desc: Runs all checks and updates the docu. It is recommended to do this before making a commit.
3446
cmds:
3547
- task: check-changelog
3648
- task: check-version
3749
- task: tests
50+
- task: lint
3851
- task: generate-docs
3952

4053
docs:

pedantic/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
from pedantic.type_checking_logic import assert_value_matches_type, resolve_forward_ref
1010

11-
from pedantic.exceptions import NotImplementedException
12-
1311
from pedantic.env_var_logic import disable_pedantic, enable_pedantic, is_enabled
1412

1513
from pedantic.decorators.fn_deco_validate.fn_deco_validate import validate, ReturnAs

pedantic/constants.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
from typing import TypeVar as Tv, Callable
2-
1+
from collections.abc import Callable
2+
from typing import TypeVar as Tv
33

44
TYPE_VAR_METHOD_NAME = '__pedantic_m42__'
55
TYPE_VAR_ATTR_NAME = '__pedantic_a42__'
6-
TYPE_VAR_SELF = Tv('__pedantic_t42__')
6+
TYPE_VAR_SELF = Tv('__pedantic_t42__') # noqa: PLC0132
77
ATTR_NAME_GENERIC_INSTANCE_ALREADY_CHECKED = '__pedantic_g42__'
88

99
TypeVar = Tv

pedantic/env_var_logic.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
ENVIRONMENT_VARIABLE_NAME = 'ENABLE_PEDANTIC'
44

55

6-
def enable_pedantic() -> None:
6+
def enable_pedantic() -> None: # noqa: D103
77
os.environ[ENVIRONMENT_VARIABLE_NAME] = '1'
88

99

10-
def disable_pedantic() -> None:
10+
def disable_pedantic() -> None: # noqa: D103
1111
os.environ[ENVIRONMENT_VARIABLE_NAME] = '0'
1212

1313

14-
def is_enabled() -> bool:
14+
def is_enabled() -> bool: # noqa: D103
1515
if ENVIRONMENT_VARIABLE_NAME not in os.environ:
1616
return True
1717

pedantic/exceptions.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
1-
2-
class NotImplementedException(Exception):
3-
pass
4-
5-
6-
class PedanticException(Exception):
7-
pass
1+
class PedanticException(Exception): # noqa: N818
2+
"""The base exception class for all Pedantic exceptions."""
83

94

105
class PedanticTypeCheckException(PedanticException):
11-
pass
6+
"""Raised if a type hint is incorrect."""
127

138

149
class PedanticDocstringException(PedanticException):
15-
pass
10+
"""Raised if the docstring is invalid e.g., wrong types"""
1611

1712

1813
class PedanticOverrideException(PedanticException):
19-
pass
14+
"""Raised when a child class overrides a method that the parent class does not have."""
2015

2116

2217
class PedanticCallWithArgsException(PedanticException):
23-
pass
18+
"""Raised if a function is called with kwargs but is called with args."""
2419

2520

2621
class PedanticTypeVarMismatchException(PedanticException):
27-
pass
22+
"""Raised if a TypeVar type conflict happens."""

pedantic/get_context.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import sys
2-
from typing import Type, Dict, List
32

43

5-
def get_context(depth: int = 1, increase_depth_if_name_matches: List[str] = None) -> Dict[str, Type]:
4+
def get_context(depth: int = 1, increase_depth_if_name_matches: list[str] | None = None) -> dict[str, type]:
65
"""
7-
Get the context of a frame at the given depth of the current call stack.
8-
See also: https://docs.python.org/3/library/sys.html#sys._getframe
6+
Get the context of a frame at the given depth of the current call stack.
7+
8+
See also: https://docs.python.org/3/library/sys.html#sys._getframe
99
"""
1010

11-
frame = sys._getframe(depth)
11+
frame = sys._getframe(depth) # noqa: SLF001
1212
name = frame.f_code.co_name
1313

1414
if name in (increase_depth_if_name_matches or []):
15-
frame = sys._getframe(depth + 1)
15+
frame = sys._getframe(depth + 1) # noqa: SLF001
1616

1717
return {**frame.f_globals, **frame.f_locals}

pedantic/mixins/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
11
from .generic_mixin import GenericMixin
2-
from .with_decorated_methods import create_decorator, DecoratorType, WithDecoratedMethods
2+
from .with_decorated_methods import DecoratorType, WithDecoratedMethods, create_decorator
3+
4+
__all__ = [
5+
'DecoratorType',
6+
'GenericMixin',
7+
'WithDecoratedMethods',
8+
'create_decorator',
9+
]

0 commit comments

Comments
 (0)