Skip to content

Commit

Permalink
Define & ensure code style
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandreDecan committed Oct 20, 2024
1 parent ce03238 commit 6763d5e
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 63 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
## 2.6.1 (not yet released)

### Added
- Add a `__version__` attribute relying on `importlib.metadata`.
- A `__version__` attribute relying on `importlib.metadata`.

### Changed
- Drop official support for Python 3.8.
- Switch from `black` to `ruff` for code style.
- Fully migrate to a `pyproject.toml`-based project.
- Drop official support for Python 3.8.
- Ensure code style consistency (see selected rules in `pyproject.toml`).


## 2.6.0 (2024-10-17)
Expand Down
11 changes: 5 additions & 6 deletions portion/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from importlib.metadata import version
import importlib.metadata

from .api import create_api
from .const import Bound, inf
from .interval import Interval, AbstractDiscreteInterval
from .func import iterate, open, closed, openclosed, closedopen, empty, singleton
from .io import from_string, to_string, from_data, to_data
from .dict import IntervalDict

from .func import closed, closedopen, empty, iterate, open, openclosed, singleton
from .interval import AbstractDiscreteInterval, Interval
from .io import from_data, from_string, to_data, to_string

__all__ = [
"create_api",
Expand All @@ -32,4 +31,4 @@
CLOSED = Bound.CLOSED
OPEN = Bound.OPEN

__version__ = version("portion")
__version__ = importlib.metadata.version("portion")
19 changes: 8 additions & 11 deletions portion/api.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import functools
import importlib
import importlib.util
import importlib.machinery
import importlib.util

from .const import Bound, inf
from .func import iterate, open, closed, openclosed, closedopen, empty, singleton
from .io import from_string, to_string, from_data, to_data
from .dict import IntervalDict

from .func import closed, closedopen, empty, iterate, open, openclosed, singleton
from .io import from_data, from_string, to_data, to_string

__all__ = ["create_api"]

Expand All @@ -23,7 +22,8 @@ def partial(wrapped, *args, **kwargs):


def create_api(interval, *, interval_dict=None, name=None):
"""
"""Create a spe
Dynamically create a module whose API is similar to the one of portion, but
configured to use given Interval class. Unless specified, a new IntervalDict
subclass is automatically generated to use given Interval subclass.
Expand All @@ -35,16 +35,13 @@ def create_api(interval, *, interval_dict=None, name=None):
:param interval_dict: a subclass of IntervalDict.
:param name: the name of the new module.
"""
if name is None:
module_name = "portion_" + interval.__name__
else:
module_name = name
module_name = "portion_" + interval.__name__ if name is None else name

if interval_dict is None:
interval_dict = type(
interval.__name__ + "Dict",
(IntervalDict,),
dict(_klass=interval),
{"_klass": interval},
)

objects = {
Expand All @@ -70,7 +67,7 @@ def create_api(interval, *, interval_dict=None, name=None):
importlib.machinery.ModuleSpec(module_name, None)
)

module.__all__ = list(objects.keys())
# module.__all__ = list(objects.keys())
for name, obj in objects.items():
setattr(module, name, obj)

Expand Down
2 changes: 1 addition & 1 deletion portion/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class _Singleton:

def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super(_Singleton, cls).__new__(cls)
cls.__instance = super().__new__(cls)
return cls.__instance


Expand Down
40 changes: 20 additions & 20 deletions portion/dict.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import contextlib
from collections.abc import Mapping, MutableMapping

from sortedcontainers import SortedDict
from collections.abc import MutableMapping, Mapping

from .const import Bound
from .interval import Interval

Expand All @@ -11,19 +14,20 @@ def _sortkey(i):

class IntervalDict(MutableMapping):
"""
An IntervalDict is a dict-like data structure that maps from intervals to data,
where keys can be single values or Interval instances.
An IntervalDict is a dict-like data structure that maps from intervals to data,where
keys can be single values or Interval instances.
When keys are Interval instances, its behaviour merely corresponds to range queries
and it returns IntervalDict instances corresponding to the subset of values covered
by the given interval. If no matching value is found, an empty IntervalDict is
returned.
When keys are Interval instances, its behaviour merely corresponds to
range queries and it returns IntervalDict instances corresponding to the
subset of values covered by the given interval. If no matching value is
found, an empty IntervalDict is returned.
When keys are "single values", its behaviour corresponds to the one of Python
built-in dict. When no matchin value is found, a KeyError is raised.
built-in dict. When no matching value is found, a KeyError is raised.
Note that this class does not aim to have the best performance, but is
provided mainly for convenience. Its performance mainly depends on the
number of distinct values (not keys) that are stored.
Note that this class does not aim to have the best performance, but is provided
mainly for convenience. Its performance mainly depends on the number of distinct
values (not keys) that are stored.
"""

__slots__ = ("_storage",)
Expand Down Expand Up @@ -165,10 +169,8 @@ def pop(self, key, default=None):
return value
else:
value = self.get(key, default)
try:
with contextlib.suppress(KeyError):
del self[key]
except KeyError:
pass
return value

def popitem(self):
Expand Down Expand Up @@ -238,7 +240,7 @@ def combine(self, other, how, *, missing=..., pass_interval=False):
:param other: another IntervalDict instance.
:param how: a function combining two values.
:param missing: if set, use this value for missing values when calling "how".
:param pass_interval: if set, provide the current interval to the "how" function.
:param pass_interval: if set, provide the current interval to the how function.
:return: a new IntervalDict instance.
"""
new_items = []
Expand Down Expand Up @@ -280,7 +282,7 @@ def as_dict(self, atomic=False):
:return: a Python dict.
"""
if atomic:
d = dict()
d = {}
for interval, v in self._storage.items():
for i in interval:
d[i] = v
Expand Down Expand Up @@ -399,10 +401,8 @@ def __contains__(self, key):
return key in self.domain()

def __repr__(self):
return "{}{}{}".format(
"{",
", ".join("{!r}: {!r}".format(i, v) for i, v in self.items()),
"}",
return "{{{}}}".format(
", ".join(f"{i!r}: {v!r}" for i, v in self.items()),
)

def __eq__(self, other):
Expand Down
4 changes: 2 additions & 2 deletions portion/func.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import operator

from functools import partial
from .interval import Interval

from .const import Bound, inf
from .interval import Interval


def open(lower, upper, *, klass=Interval):
Expand Down
27 changes: 14 additions & 13 deletions portion/interval.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import warnings

from collections import namedtuple
from .const import Bound, inf

from .const import Bound, inf

Atomic = namedtuple("Atomic", ["left", "lower", "upper", "right"])

Expand Down Expand Up @@ -41,7 +40,7 @@ def __init__(self, *intervals):
:param intervals: zero, one or more intervals.
"""
self._intervals = list()
self._intervals = []

for interval in intervals:
if isinstance(interval, Interval):
Expand Down Expand Up @@ -283,9 +282,7 @@ def apply(self, func):
elif isinstance(value, tuple):
intervals.append(self.__class__.from_atomic(*value))
else:
raise TypeError(
"Unsupported return type {} for {}".format(type(value), value)
)
raise TypeError(f"Unsupported return type {type(value)} for {value}")

return self.__class__(*intervals)

Expand Down Expand Up @@ -332,7 +329,7 @@ def overlaps(self, other):
return True
return False
else:
raise TypeError("Unsupported type {} for {}".format(type(other), other))
raise TypeError(f"Unsupported type {type(other)} for {other}")

def intersection(self, other):
"""
Expand Down Expand Up @@ -453,7 +450,7 @@ def __and__(self, other):
# i_current can still intersect next o
o_current = next(o_iter, None)
else:
assert False
raise AssertionError()

return self.__class__(*intersections)

Expand Down Expand Up @@ -557,7 +554,8 @@ def __lt__(self, other):
return self.upper < other.lower
else:
warnings.warn(
"Comparing an interval and a value is deprecated. Convert value to singleton first.",
"Comparing an interval and a value is deprecated. "
"Convert value to singleton first.",
DeprecationWarning,
)
return not self.empty and (
Expand All @@ -575,7 +573,8 @@ def __gt__(self, other):
return self.lower > other.upper
else:
warnings.warn(
"Comparing an interval and a value is deprecated. Convert value to singleton first.",
"Comparing an interval and a value is deprecated. "
"Convert value to singleton first.",
DeprecationWarning,
)
return not self.empty and (
Expand All @@ -593,7 +592,8 @@ def __le__(self, other):
return self.upper < other.upper
else:
warnings.warn(
"Comparing an interval and a value is deprecated. Convert value to singleton first.",
"Comparing an interval and a value is deprecated. "
"Convert value to singleton first.",
DeprecationWarning,
)
return not self.empty and self.upper <= other
Expand All @@ -609,13 +609,14 @@ def __ge__(self, other):
return self.lower > other.lower
else:
warnings.warn(
"Comparing an interval and a value is deprecated. Convert value to singleton first.",
"Comparing an interval and a value is deprecated. "
"Convert value to singleton first.",
DeprecationWarning,
)
return not self.empty and self.lower >= other

def __hash__(self):
return hash(tuple([self.lower, self.upper]))
return hash((self.lower, self.upper))

def __repr__(self):
if self.empty:
Expand Down
14 changes: 6 additions & 8 deletions portion/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,10 @@ def from_string(
:return: an interval.
"""

re_left_boundary = r"(?P<left>{}|{})".format(left_open, left_closed)
re_right_boundary = r"(?P<right>{}|{})".format(right_open, right_closed)
re_bounds = r"(?P<lower>{bound})({sep}(?P<upper>{bound}))?".format(
bound=bound, sep=sep
)
re_interval = r"{}(|{}){}".format(re_left_boundary, re_bounds, re_right_boundary)
re_left_boundary = rf"(?P<left>{left_open}|{left_closed})"
re_right_boundary = rf"(?P<right>{right_open}|{right_closed})"
re_bounds = rf"(?P<lower>{bound})({sep}(?P<upper>{bound}))?"
re_interval = rf"{re_left_boundary}(|{re_bounds}){re_right_boundary}"

intervals = []
has_more = True
Expand All @@ -63,7 +61,7 @@ def _convert(bound):
while has_more:
match = re.match(re_interval, string)
if match is None:
raise ValueError('"{}" cannot be parsed to an interval.'.format(source))
raise ValueError(f'"{source}" cannot be parsed to an interval.')

# Parse atomic interval
group = match.groupdict()
Expand All @@ -86,7 +84,7 @@ def _convert(bound):
if len(string) > 0:
match = re.match(disj, string)
if match is None:
raise ValueError('"{}" cannot be parsed to an interval.'.format(source))
raise ValueError(f'"{source}" cannot be parsed to an interval.')
else:
string = string[match.end() :]
else:
Expand Down
16 changes: 16 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,21 @@ test = ["pytest ~= 7.0", "coverage ~= 6.0", "ruff >= 0.6.9"]
[tool.ruff]
extend-exclude = ["tests/"]

[tool.ruff.lint]
select = [
"F", # pyflakes
"E", # pycodestyle
"W", # pycodestyle
"UP", # pyupgrade
"I", # Sort imports
"N", # pep8-naming
"B", # flake8-bugbear
"SIM", # flake8-simplify
"C4", # flake8-comprehensions
"PIE", # flake8-pie
"Q", # flake8-quotes
]
ignore = ["B028"]

[tool.setuptools]
packages = ["portion"]

0 comments on commit 6763d5e

Please sign in to comment.