-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Frost Ming <[email protected]>
- Loading branch information
Showing
30 changed files
with
3,198 additions
and
311 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
# pkg-logical | ||
# Dep-Logic | ||
|
||
Logical operational specifiers and markers. | ||
Python dependency specifications supporting logical operations |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,19 @@ | ||
[project] | ||
name = "pkg-logical" | ||
version = "0.1.0" | ||
description = "Logical operational specifiers and markers" | ||
name = "dep-logic" | ||
description = "Python dependency specifications supporting logical operations" | ||
authors = [ | ||
{name = "Frost Ming", email = "[email protected]"}, | ||
] | ||
dependencies = [ | ||
"packaging>=22", | ||
] | ||
requires-python = ">=3.10" | ||
requires-python = ">=3.8" | ||
readme = "README.md" | ||
license = {text = "Apache-2.0"} | ||
dynamic = ["version"] | ||
|
||
[tool.pdm.version] | ||
source = "scm" | ||
|
||
[build-system] | ||
requires = ["pdm-backend"] | ||
|
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
# Adapted from poetry/core/version/markers.py | ||
# The original work is published under the MIT license. | ||
# Copyright (c) 2020 Sébastien Eustace | ||
# Adapted by Frost Ming (c) 2023 | ||
|
||
from __future__ import annotations | ||
|
||
import functools | ||
from typing import TYPE_CHECKING | ||
|
||
from packaging.markers import InvalidMarker as _InvalidMarker | ||
from packaging.markers import Marker as _Marker | ||
|
||
from dep_logic.markers.any import AnyMarker | ||
from dep_logic.markers.base import BaseMarker | ||
from dep_logic.markers.empty import EmptyMarker | ||
from dep_logic.markers.multi import MultiMarker | ||
from dep_logic.markers.single import MarkerExpression | ||
from dep_logic.markers.union import MarkerUnion | ||
from dep_logic.utils import get_reflect_op | ||
|
||
if TYPE_CHECKING: | ||
from typing import List, Literal, Tuple, Union | ||
|
||
from packaging.markers import Op, Value, Variable | ||
|
||
_ParsedMarker = Tuple[Variable, Op, Value] | ||
_ParsedMarkers = Union[ | ||
_ParsedMarker, List[Union["_ParsedMarkers", Literal["or", "and"]]] | ||
] | ||
|
||
|
||
__all__ = [ | ||
"parse_marker", | ||
"from_pkg_marker", | ||
"InvalidMarker", | ||
"BaseMarker", | ||
"AnyMarker", | ||
"EmptyMarker", | ||
"MarkerExpression", | ||
"MarkerUnion", | ||
"MultiMarker", | ||
] | ||
|
||
|
||
class InvalidMarker(ValueError): | ||
""" | ||
An invalid marker was found, users should refer to PEP 508. | ||
""" | ||
|
||
|
||
@functools.lru_cache(maxsize=None) | ||
def parse_marker(marker: str) -> BaseMarker: | ||
if marker == "<empty>": | ||
return EmptyMarker() | ||
|
||
if not marker or marker == "*": | ||
return AnyMarker() | ||
try: | ||
parsed = _Marker(marker) | ||
except _InvalidMarker as e: | ||
raise InvalidMarker(str(e)) from e | ||
|
||
markers = _build_markers(parsed._markers) | ||
|
||
return markers | ||
|
||
|
||
def from_pkg_marker(marker: _Marker) -> BaseMarker: | ||
return _build_markers(marker._markers) | ||
|
||
|
||
def _build_markers(markers: _ParsedMarkers) -> BaseMarker: | ||
from packaging.markers import Variable | ||
|
||
if isinstance(markers, tuple): | ||
if isinstance(markers[0], Variable): | ||
name, op, value, reversed = ( | ||
str(markers[0]), | ||
str(markers[1]), | ||
str(markers[2]), | ||
False, | ||
) | ||
else: | ||
# in reverse order | ||
name, op, value, reversed = ( | ||
str(markers[2]), | ||
get_reflect_op(str(markers[1])), | ||
str(markers[0]), | ||
True, | ||
) | ||
return MarkerExpression(name, op, value, reversed) | ||
or_groups: list[BaseMarker] = [AnyMarker()] | ||
for item in markers: | ||
if item == "or": | ||
or_groups.append(AnyMarker()) | ||
elif item == "and": | ||
continue | ||
else: | ||
or_groups[-1] &= _build_markers(item) | ||
return MarkerUnion.of(*or_groups) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from dep_logic.markers.base import BaseMarker | ||
|
||
|
||
class AnyMarker(BaseMarker): | ||
def __and__(self, other: BaseMarker) -> BaseMarker: | ||
return other | ||
|
||
__rand__ = __and__ | ||
|
||
def __or__(self, other: BaseMarker) -> BaseMarker: | ||
return self | ||
|
||
__ror__ = __or__ | ||
|
||
def is_any(self) -> bool: | ||
return True | ||
|
||
def evaluate(self, environment: dict[str, str] | None = None) -> bool: | ||
return True | ||
|
||
def without_extras(self) -> BaseMarker: | ||
return self | ||
|
||
def exclude(self, marker_name: str) -> BaseMarker: | ||
return self | ||
|
||
def only(self, *marker_names: str) -> BaseMarker: | ||
return self | ||
|
||
def __str__(self) -> str: | ||
return "" | ||
|
||
def __repr__(self) -> str: | ||
return "<AnyMarker>" | ||
|
||
def __hash__(self) -> int: | ||
return hash("any") | ||
|
||
def __eq__(self, other: object) -> bool: | ||
if not isinstance(other, BaseMarker): | ||
return NotImplemented | ||
|
||
return isinstance(other, AnyMarker) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from __future__ import annotations | ||
|
||
from abc import ABCMeta, abstractmethod | ||
from typing import Any | ||
|
||
|
||
class BaseMarker(metaclass=ABCMeta): | ||
@property | ||
def complexity(self) -> tuple[int, int]: | ||
""" | ||
The first number is the number of marker expressions, | ||
and the second number is 1 if the marker is single-like. | ||
""" | ||
return 1, 1 | ||
|
||
@abstractmethod | ||
def __and__(self, other: Any) -> BaseMarker: | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def __or__(self, other: Any) -> BaseMarker: | ||
raise NotImplementedError | ||
|
||
def is_any(self) -> bool: | ||
return False | ||
|
||
def is_empty(self) -> bool: | ||
return False | ||
|
||
@abstractmethod | ||
def evaluate(self, environment: dict[str, str] | None = None) -> bool: | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def without_extras(self) -> BaseMarker: | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def exclude(self, marker_name: str) -> BaseMarker: | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def only(self, *marker_names: str) -> BaseMarker: | ||
raise NotImplementedError | ||
|
||
def __repr__(self) -> str: | ||
return f"<{self.__class__.__name__} {self}>" | ||
|
||
@abstractmethod | ||
def __str__(self) -> str: | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def __hash__(self) -> int: | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def __eq__(self, other: object) -> bool: | ||
raise NotImplementedError |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from dep_logic.markers.base import BaseMarker | ||
|
||
|
||
class EmptyMarker(BaseMarker): | ||
def __and__(self, other: BaseMarker) -> BaseMarker: | ||
return self | ||
|
||
__rand__ = __and__ | ||
|
||
def __or__(self, other: BaseMarker) -> BaseMarker: | ||
return other | ||
|
||
__ror__ = __or__ | ||
|
||
def is_empty(self) -> bool: | ||
return True | ||
|
||
def evaluate(self, environment: dict[str, str] | None = None) -> bool: | ||
return False | ||
|
||
def without_extras(self) -> BaseMarker: | ||
return self | ||
|
||
def exclude(self, marker_name: str) -> BaseMarker: | ||
return self | ||
|
||
def only(self, *marker_names: str) -> BaseMarker: | ||
return self | ||
|
||
def __str__(self) -> str: | ||
return "<empty>" | ||
|
||
def __repr__(self) -> str: | ||
return "<EmptyMarker>" | ||
|
||
def __hash__(self) -> int: | ||
return hash("empty") | ||
|
||
def __eq__(self, other: object) -> bool: | ||
if not isinstance(other, BaseMarker): | ||
return NotImplemented | ||
|
||
return isinstance(other, EmptyMarker) |
Oops, something went wrong.